理解 WSRF之三:使用 WS-Notification
谁应该学习本教程?
本教程是一个由 4 部分组成的系列教程中的第 3 部分,该系列介绍 WS-Resource Framework (WSRF) 背后的概念。这里,我们要介绍 WS-Notification,它使我们可以在 Web 服务的无状态环境中模拟事件驱动的应用程序。
作为 Web 服务开发人员,如果想要提供一种方式,让服务可以被通知它们了解或影响的范围之外的事件,就应该阅读本教程。网格计算和 WSRF 的用户尤其会对本文感兴趣。
本教程讨论 WS-Notification 系列规范定义的结构和消息。这些规范为 Web 服务客户机定义一种标准的方式,以“订阅”特定的主题,并接收各种事件的通知。在本教程中,我们将会学习:
l Notification、NotificationProducer、NotificationConsumer,以及它们相互之间所充当的角色。
l 定义并创建一个 WS-Resource,它也是一个 NotificationConsumer。
l 向现有 WS-Resource(注意,就是第 1 和 2 部分中的人造卫星例子)添加 NotificationProducer 功能。
l 订阅一个主题,并管理该订阅。
l 解除订阅主题。
l 主题的角色。
l TopicSpace 和 TopicTree。
l WSRF 中使用通知的方式。
注意,WSN 作为一系列规范,定义了 WSDL 文件的结构,该文件描述以上这些操作。该 WSDL 文件然后可以被任何语言的实现所使用。本教程描述 WSDL 文件的创建,并展示产生的 SOAP 消息。
一定要明白,WSN 系列规范只定义应该做什么,而不定义应该如何做。这里描述的概念的实际实现留给了应用程序去完成。在本系列的第 4 部分中,我们将使用 Globus Alliance 提供的 Java WSRF Core 类来讨论该实现。
第一节:概述
什么是通知?我们为什么关心通知?
无状态系统(如 Web 服务)的特征是,请求时可用的惟一信息就是该请求中实际包含的信息。大多数时候,只需要这一信息,但是也有不够的时候。
例如,我们已经用两个教程介绍了 WS-Resource,即有状态资源和 Web 服务的组合。WS-Resource 很方便,因为可以通过改变其属性来操纵它们。我们创建了一个人造卫星的例子,可以通过改变它的高度(altitude)和位置(position)属性来移动它。WSRF 中一种常见的情况是,一个资源需要知道另一个资源的属性何时发生了改变。监控组件需要知道我们移动了人造卫星,或者数据收集组件需要知道何时一批新的数据是可用的。
WSN 就用于此目的。我们可以创建一个结构,在此结构中,客户机组件可以“订阅”特定的主题(比如人造卫星高度的改变),当事件发生时,这些组件会收到一个通知消息。
简而言之,WSN 让我们可以使用 Web 服务来模拟基于事件的系统。
什么是 WSRF?
这是一个关于 WSRF(即 WS-Resource Framework)的系列,所以有必要花点时间解释一下什么是 WSRF,以及为什么它对于理解 WSN 很重要。
WSRF 背后的主要概念是 WS-Resource 的概念。WS-Resource 是有状态资源(比如文件或数据库)和与之交互的 Web 服务的组合。资源是由很多定义其状态 的资源属性 定义的。改变一个这样的属性,就改变了状态,而且这在交互之间是持久的。
WSRF 定义很多可以在 WS-Resource 上执行的操作,比如获得和设置它的属性,以及将它添加到一个具有其他类似服务的 ServiceGroup。也可以使用 WSRF 来销毁 WS-Resource 或者设置它的“终止日期”。
所有这些交互都是通过一个 WSDL 文件来定义的。
WSN 角色
现在我们来讨论 Web 服务通知(Web service notification,WSN)。WSN 定义几个基本的角色,包括 NotificationProducer和NotificationConsumer。在最简单的情况下,NotificationConsumer 联系 NotificationProducer 以获得特定主题的Subscription。当 NotificationProducer具有与该主题相关的一个消息时,它会将该消息发送给 NotificationConsumer。
这就是 WSN 工作的最简单方式。有时要复杂一些。例如,NotificationConsumer不必直接订阅。您可能具有一个 Subscriber对象,它为 NotificationConsumer 创建 Subscription。或者您可能具有一个 Publisher 对象,它不能处理 NotificationProducer 的所有任务。在这种情况下,NotificationProducer 将它的消息发送给 NotificationBroker,后者又将消息发送给 NotificationConsumer。(这个最后的配置是WS-BrokeredNotification规范的一部分,超出了本教程的范畴。)
下面来看看我们要讨论的内容。
为了演示通知是如何工作的,我们要创建一个系统,其中有两种类型的事件。一个事件是在人造卫星中的恒星计数器达到某个阈值时被触发—即 thresholdReached 情况;另一个事件是在人造卫星本身中的软件必须重置时被触发—即 counterReset 事件。
假设我们具有两个 NotificationConsumer。一个是科研人员,他只对 thresholdReached 情况感兴趣,另一个是系统管理员,他需要所有主题的通知。
科研人员将会订阅一个主题,接收消息,并保持订阅的更新。管理员将订阅两个主题,接收消息,最终暂停并继续订阅。
我们还要介绍如何构造主题,这有助于过滤内容。
第二节:情况和通知
情况
在开始发送任何通知之前,我们需要定义通知是什么,以及为什么要发送它们。
通知是为响应情况(situation) 而发送的。一个情况是对某人或某事有兴趣的一个或一组事件。一旦我们发送一个通知,该通知就被认为表示我们所报告的情况。
当然,并不是所有消费者都需要关于单个情况的单个通知。相反,当一个 NotificationConsumer 订阅 NotificationProducer 时,他就指定了一个感兴趣的主题。在本例中,我们可以设置一个 topicSpace 具有表示我们的两个情况的主题:
<wstop:topicSpace name="SatelliteTopics"
targetNamespace="http://example.com/satellite"
xmlns:wstop="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-Topics-1.2-draft-01.xsd">
<wstop:topic name="counterReset" />
<wstop:topic name="thresholdReached" />
</wstop:topicSpace>
在 主题 一节将会学习更多关于主题的信息,但是这个简单的 topicSpace 现在还是管用的。
定义通知
WSN 为通知指定标准的格式。Notify消息包括一个 NotificationMessage,后者可以包括诸如这样的信息:消息是为哪个主题而产生的,以及消息源的端点引用(或者地址)。考虑下面这个例子:
<wsnt:Notify>
<wsntw:NotificationMessage>
<wsnt:Topic
Dialect="http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple">
sat:thresholdReached
</wsnt:Topic>
<wsnt:ProducerReference>
<wsa:EndpointReference
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:sat="http://example.org/satelliteSystem">
<wsa:Address>http://example.com/satellite</wsa:Address>
<wsa:ReferenceProperties>
<sat:ResourceId>RES2883</sat:ResourceId>
</wsa:ReferenceProperties>
</wsa:EndpointReference>
</wsnt:ProducerReference>
<wsnt:Message>
<sat:ThresholdReachedMessage>
<sat:MessageTitle>Threshold reached</sat:MessageTitle>
<sat:MessageContent>
The satellite has reached a new milestone in
counting stars. Please view the current data for
status.
</sat:MessageContent>
</sat:ThresholdReachedMessage>
</wsnt:Message>
</wsntw:NotificationMessage>
</wsnt:Notify>
除了额外的信息(比如主题和生产者)之外,使用该格式还使系统能够在单个通知中发送多个消息。
WS-Notification也使用不是这种格式的“原始”通知,但是使用这样的通知需要接收服务指定可以接收的每种消息类型。
第三节:NotificationConsumer 和 NotificationProducer
NotificationConsumer
在本节中,我们将要定义与通知有关的两个主要角色:NotificationConsumer 和 NotificationProducer。我们从 NotificationConsumer 开始介绍。
为了成为 NotificationConsumer,资源必须实现单个操作:Notify。现在,让消费者具有 Notify 操作似乎有些愚蠢,因为它实际上只是接收通知,但是请记住,不是 NotificationConsumer 来调用该操作。NotificationProducer 调用消费者服务的 Notify 操作,以便将通知发送给消费者。
只是充当 NotificationConsumer 的服务实际上并不存储任何信息,所以它不具有自己的任何资源属性,实际上,甚至不必是 WS-Resource。如果是向现有的 WS-Resource 添加 NotificationConsumer 功能,我们只要使用现有的资源属性文档即可 —— 就像在 NotificationProducer 一节中创建 NotificationProducer 时所要做的一样 —— 但是目前,我们还要创建一个骨架服务,Notify 操作就位于其上。
定义 NotificationConsumer
NotificationConsumer 不必是 WS-Resource,所以我们只需要创建一个 WSDL 文件。该文件定义一个 Web 服务,该服务实现 WS-BaseN.wsdl 文件中定义的 NotificationConsumer portType,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="SatelliteAdministrator"
targetNamespace="http://example.com/satellite"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://example.com/satellite"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsntw="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
location="WS-BaseN.wsdl" />
<types>
<xsd:schema targetNamespace="http://example.com/satellite"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import
namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing"
schemaLocation="WS-Addressing.xsd" />
</xsd:schema>
</types>
<binding name="ConsumerSoapBinding"
type="wsntw:NotificationConsumer">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="Notify">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="ConsumerService">
<port name="ConsumerPort" binding="tns:ConsumerSoapBinding">
<soap:address
location="http://example.com/satelliteMessageConsumer" />
</port>
</service>
</definitions>
这里,我们创建 ConsumerService,它使用 ConsumerSoapBinding 实现 WS-BaseN.wsdl 文件中定义的 NotificationConsumer portType。
注意,如果是为一个接收原始通知消息的服务创建 WSDL 文件,我们就要在使用适当消息时定义 Notify 操作。
NotificationProducer
NotificationProducer 比 NotificationConsumer 要稍微复杂一些。其一,它需要几个资源属性,所以它必须是 WS-Resource,这与纯粹的服务相反。这些资源属性定义生产者接受订阅的主题。例如,生产者的资源属性文档可能类似于下面这样:
<wsn:NotificationProducerRP
xmlns:wsn="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<wsn:Topic>thresholdReached</wsn:Topic>
<wsn:Topic>counterReset</wsn:Topic>
<wsn:FixedTopicSet>false</wsn:FixedTopicSet>
<wsn:TopicExpressionDialects>
http://docs.oasis-open.org/wsn/2004/06/TopicExpressi on/Simple
</wsn:TopicExpressionDialects>
</wsn:NotificationProducerRP>
在本例中,NotificationProducer 接受我们前面定义的两个主题的订阅。稍后,在 主题 一节中,我们将进一步谈论在生产者内管理主题。
作为 WS-Resource,NotificationProducer 必须支持所需的消息交换,比如 GetResourceProperty。作为 NotificationProducer,它必须支持 Subscribe 和 GetCurrentMessage 操作。
现在我们将这些功能添加到一个现有 WS-Resource。
将人造卫星转化成 NotificationProducer:
在本例中,我们想要向现有的人造卫星服务添加 NotificationProducer 功能,所以我们要像 WSDL 文件中定义的一样,向现有资源属性文档添加生产者相关的资源属性。(可从 参考资料 一节下载人造卫星 WSDL 文件的当前版本。)
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Satellite"
targetNamespace="http://example.com/satellite"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://example.com/satellite"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsrp="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
xmlns:wsrpwsdl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
xmlns:wsrl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
xmlns:wsrlwsdl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
xmlns:wssg="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ServiceGroup-1.2-draft-01.xsd"
xmlns:wssgwsdl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ServiceGroup-1.2-draft-01.wsdl"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
xmlns:wsntw="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
location="WS-ResourceProperties.wsdl" />
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.wsdl"
location="WS-ResourceLifetime.wsdl" />
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ServiceGroup-1.2-draft-01.wsdl"
location="WS-ServiceGroup.wsdl" />
<wsdl:import
namespace="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
location="WS-BaseN.wsdl" />
<types>
<xsd:schema targetNamespace="http://example.com/satellite"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import
namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing"
schemaLocation="WS-Addressing.xsd" />
<xsd:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
schemaLocation="WS-ResourceLifetime.xsd" />
<xsd:element name="createSatellite">
<xsd:complexType />
</xsd:element>
<xsd:element name="createSatelliteResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="wsa:EndpointReference" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="createSatGroup">
<xsd:complexType />
</xsd:element>
<xsd:element name="createSatGroupResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="wsa:EndpointReference" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="latitude" type="xsd:float" />
<xsd:element name="longitude" type="xsd:float" />
<xsd:element name="altitude" type="xsd:float" />
<xsd:element name="pitch" type="xsd:float" />
<xsd:element name="yaw" type="xsd:float" />
<xsd:element name="roll" type="xsd:float" />
<xsd:element name="focalLength" type="xsd:float" />
<xsd:element name="currentView" type="xsd:string" />
<xsd:element name="GenericSatelliteProperties">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="latitude" minOccurs="1" maxOccurs="1" />
<xsd:element ref="longitude" minOccurs="1" maxOccurs="1" />
<xsd:element ref="altitude" minOccurs="1" maxOccurs="1" />
<xsd:element ref="pitch" minOccurs="1" maxOccurs="1" />
<xsd:element ref="yaw" minOccurs="1" maxOccurs="1" />
<xsd:element ref="roll" minOccurs="1" maxOccurs="1" />
<xsd:element ref="focalLength" minOccurs="1" maxOccurs="1" />
<xsd:element ref="currentView" minOccurs="1" maxOccurs="1" />
<xsd:element ref="wsrl:CurrentTime" minOccurs="1" maxOccurs="1" />
<xsd:element ref="wsrl:TerminationTime" minOccurs="1" maxOccurs="1" />
<xsd:element ref="wsnt:Topic" minOccurs="1"
maxOccurs="unbounded" />
<xsd:element ref="wsnt:FixedTopicSet"
minOccurs="1" maxOccurs="1" />
<xsd:element ref="wsnt:TopicExpressionDialects"
minOccurs="1" maxOccurs="unbounded" />
<xsd:any />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</types>
<message name="CreateSatelliteRequest">
...
现在,人造卫星可以管理 NotificationProducer 必须管理的数据了,但是我们还需要添加它必须执行的操作。
将人造卫星转化为 NotificationProducer:操作
WS-BaseN.wsdl 文件定义 NotificationProducer portType,但是将它与一个叫做 NotificationProducerRP 的资源属性相关联,所以我们必须直接为人造卫星声明操作,以实现这些操作:
...<portType name="SatellitePortType"
wsrp:ResourceProperties="tns:GenericSatelliteProperties">
...
<operation name="renewSatellite">
<input message="wsrlwsdl:SetTerminationTimeRequest"
wsa:Action="http://docs.oasis-open.org/wsrf/2004/06/WS-ResourceLifetime/SetTerminationTime" />
<output message="wsrlwsdl:SetTerminationTimeResponse"
wsa:Action="http://docs.oasis-open.org/wsrf/2004/06/WS-ResourceLifetime/SetTerminationTimeResponse" />
</operation>
<wsdl:operation name="Subscribe">
<wsdl:input message="wsntw:SubscribeRequest"
wsa:Action="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification/Subscribe" />
<wsdl:output message="wsntw:SubscribeResponse"
wsa:Action="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification/SubscribeResponse" />
<wsdl:fault name="ResourceUnknownFault"
message="wsntw:ResourceUnknownFault" />
<wsdl:fault name="SubscribeCreationFailedFault"
message="wsntw:SubscribeCreationFailedFault" />
<wsdl:fault name="TopicPathDialectUnknownFault"
message="wsntw:TopicPathDialectUnknownFault" />
</wsdl:operation>
<wsdl:operation name="GetCurrentMessage">
<wsdl:input message="wsntw:GetCurrentMessageRequest"
wsa:Action="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification/GetCurrentMessage" />
<wsdl:output message="wsntw:GetCurrentMessageResponse"
wsa:Action="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification/GetCurrentMessageResponse" />
<wsdl:fault name="ResourceUnknownFault"
message="wsntw:ResourceUnknownFault" />
<wsdl:fault name="InvalidTopicExpressionFault"
message="wsntw:InvalidTopicExpressionFault" />
<wsdl:fault name="TopicNotSupportedFault"
message="wsntw:TopicNotSupportedFault" />
<wsdl:fault name="NoCurrentMessageOnTopicFault"
message="wsntw:NoCurrentMessageOnTopicFault" />
</wsdl:operation>
</portType>
<binding name="SatelliteSoapBinding" type="tns:SatellitePortType">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
...
<operation name="renewSatellite">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="Subscribe">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="GetCurrentMessage">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
...
这里,我们直接采用 WS-BaseN.wsdl 文档中的定义。因为我们导入了该文档,所以可以使用它包含的 message 定义。因此,只需要实现 binding 中的操作了,我们已经准备就绪。
第四节:系统运转
编址 NotificationConsumer
在本节中,我们将展示在创建订阅和从 NotificationProducer 向 NotificationConsumer 发送通知过程中涉及的一些消息。
如果您一直在阅读该系列文章,您可能希望我们从创建 NotificationConsumer 开始,但是我们不会这样做,因为在本例中,NotificationConsumer 不是 WS-Resource。这里并没有说它不能是 WS-Resource,但是在本例中确实不是的。
但是这并不会妨碍什么。给 NotificationConsumer 发送消息只需要它的端点引用,正如 WS-Addressing 所定义的。(参阅 本系列的第 1 部分,以回顾 WS-Addressing。)如果 NotificationConsumer 是 WS-Resource 的话,我们就应该从“创建”过程获得端点引用,但是因为它不是的,所以我们可以容易地从 WSDL 文件中的信息构造它:
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/02/addressing">
<wsa:Address>http://example.com/satelliteMessageConsumer</wsa:Address>
</wsa:EndpointReference>
在本例中,EndpointReference 所需要的就是 Address 元素。若要处理 WS-Resource,它还应该包含一个 ReferenceProperties 元素,该元素为所处理的 WS-Resource 包含一些种类的标识符,但是这是惟一的区别。否则,我们将把该端点引用看成是属于 WS-Resource 的。
我们将使用该端点引用作为一个向其发送通知的地址。
创建订阅
现在,我们已准备好为 NotificationConsumer 创建订阅。为此,我们要向 NotificationProducer 发送一个“订阅”消息,其中带有关于消费者位于哪里和想要订阅的主题的信息:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/Subscribe
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satellite
</wsa:To>
<sat:SatelliteId>SAT9928</sat:SatelliteId>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:Subscribe>
<wsnt:ConsumerReference>
<wsa:EndpointReference>
<wsa:Address>
http://example.com/satelliteMessageConsumer
</wsa:Address>
</wsa:EndpointReference>
</wsnt:ConsumerReference>
<wsnt:TopicExpression
Dialect="http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple">
sat:counterReset
</wsnt:TopicExpression>
<wsnt:UseNotify>true</wsnt:UseNotify>
<wsnt:InitialTerminationTime>
2005-12-31T12:00:00
</wsnt:InitialTerminationTime>
</wsnt:Subscribe>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
我们从消息的顶部开始。首先,Header 展示 WS-Resource 的 WS-Addressing 信息,表示人造卫星将会充当 NotificationProducer,所以它将接收消息。Body 包含实际的 Subscribe 消息。
Subscribe 消息沿着消费者(在前一屏中定义)的端点引用传递,并指定消费者想要订阅 counterReset 主题。消费者也想要生产者使用 Notify 消息格式,而订阅应该一直持续到 2005-12-31T12:00:00。
我们也应该指定几个影响订阅的条件。wsnt:Precondition 元素使我们可以指定表达式 —— 通常是 XPath 表达式。当应用于通知时,为了递送通知,该表达式必须为 true。例如,当且仅当到达某个阈值(而不是任意阈值)时,需要通知科研人员。wsnt:Selector 类似于 wsnt:Precondition,只是它必须计算一个布尔值。
Subscribe 请求也可以包含一个特定于应用程序的 wsnt:SubscriptionPolicy 元素,这使得消费者可以指定诸如消息频率这样的信息。例如,应用程序可以使用该元素,让消费者可以指定他们是否想要接收单独的消息或者所有消息的单个的每日摘要。
Subscribe 消息的响应包含对实际 Subscription WS-Resource 的端点引用:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/SubscribeResponse
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteMessageConsumer
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:SubscribeResponse>
<wsnt:SubscriptionReference>
<wsa:Address>
http://example.com/satelliteSubscriptions
</wsa:Address>
<wsa:ReferenceProperties>
<sat:ResourceID>SUB3772</sat:ResourceID>
</wsa:ReferenceProperties>
</wsnt:SubscriptionReference>
</wsnt:SubscribeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
注意,Header 包含用于 NotificationConsumer 的信息。Body 包含对实际订阅的端点引用。技术上,这是对 SubscriptionManager 的端点引用,我们将会在 管理订阅 一节中讲到。但是现在,只是把它当作订阅。
请求最当前的消息
一旦消费者向生产者订阅了某个主题,他就经常想要看到该主题的最新消息。NotificationProducer 应该缓存当前消息,消费者可以使用 GetCurrentMessage 消息请求当前消息:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNot
ification/GetCurrentMessage
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satellite
</wsa:To>
<sat:SatelliteId>SAT9928</sat:SatelliteId>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:GetCurrentMessage>
<wsnt:Topic
Dialect="http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple">
sat:counterReset
</wsnt:Topic>
</wsnt:GetCurrentMessage>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
响应包含该主题的最新消息(如果有的话):
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseN-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNot
ification/GetCurrentMessageResponse
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteConsumer
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:GetCurrentMessageResponse>
<sat:MessageTitle>Satellite Reset</sat:MessageTitle>
<sat:MessageContent>
The satellite has been rebooted due to an administrator
request.
</sat:MessageContent>
</wsnt:GetCurrentMessageResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
注意,GetCurrentMessageResponse 只包含消息,而不是包含整个通知。如果不存在缓存的消息,响应就会包含一个 fault 消息。
发送通知
假设此时我们具有两个 NotificationConsumer。一个是管理员,他订阅了 counterReset 和 thresholdReached 两个主题,并且 UseNotify 等于 true。另一个是科研人员,他只订阅了 thresholdReached 主题,并且 UseNotify 等于 false。
我们进一步假设人造卫星上的计数器达到某个阈值。现在它有一个通知要针对 thresholdReached 主题发送出去。它知道具有两个订阅者。第一个是管理员,他是使用 Notify 格式订阅的,所以它使用该格式创建并发送一个通知消息:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseN-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseN
otification/Notify
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteConsumer
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:Notify>
<wsnt:NotificationMessage>
<wsnt:Topic
Dialect="http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple">
thresholdReached
</wsnt:Topic>
<wsnt:ProducerReference>
<wsa:Address>
http://example.com/satellite
</wsa:Address>
<wsa:ReferenceProperties>
<sat:SatelliteId>SAT9928</sat:SatelliteId>
</wsa:ReferenceProperties>
</wsnt:ProducerReference>
<wsnt:Message>
<sat:MessageTitle>
Threshold Reached
</sat:MessageTitle>
<sat:MessageContent>
The satellite has now counted over 10,000 stars!
</sat:MessageContent>
</wsnt:Message>
</wsnt:NotificationMessage>
</wsnt:Notify>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
NotificationConsumer 并不发送消息的响应。
NotificationProducer 也向科研人员发送原始通知。
主题的每个订阅者获得他自己的通知副本,格式是在订阅中指定的,并且假设消息符合在订阅的 Precondition 和 Selector 属性中展示的条件。
第五节:管理订阅
SubscriptionManager
在本节中,我们来看管理订阅,包括跟踪它、暂停它,以及继续它。所有这些操作都是在 SubscriptionManager 中发生的。
SubscriptionManager 是一种 WS-Resource,负责跟踪订阅,包括对 NotificationConsumer 的端点引用、主题和递送选项。针对管理员的订阅的资源属性文档类似于下面这样:
<wsnt:SubscriptionManagerRP>
<wsnt:ConsumerReference>
<wsa:EndpointReference>
<wsa:Address>
http://example.com/satelliteMessageConsumer
</wsa:Address>
</wsa:EndpointReference>
</wsnt:ConsumerReference>
<wsnt:TopicExpression>counterReset</wsnt:TopicExpression>
<wsnt:UseNotify>true</wsnt:UseNotify>
<wsnt:Precondition></wsnt:Precondition>
<wsnt:Selector></wsnt:Selector>
<wsnt:SubscriptionPolicy></wsnt:SubscriptionPolicy>
<wsnt:CreationTime>2005-02-15T04:17:58</wsnt:CreationTime>
<wsnt:TerminationTime>2005-12-31T12:00:00</wsnt:TerminationTime>
<wsrl:CurrentTime>2005-12-15T04:24:23</wsrl:CurrentTime>
</wsnt:SubscriptionManagerRP>
实际的 SubscriptionManagerRP 类型是在 WS-BaseN.wsdl 中定义的。
SubscriptionManager WSDL
SubscriptionManager 只是实现 WS-BaseN.wsdl 文件中的定义,所以创建一个 WSDL 文件以定义我们自己的实例是很直观的:
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="SatelliteResearcher"
targetNamespace="http://example.com/satellite"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://example.com/satellite"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsrp="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.xsd"
xmlns:wsrpwsdl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd"
xmlns:wsntw="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.wsdl"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
location="WS-ResourceProperties.wsdl"/>
<wsdl:import
namespace="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceProperties-1.2-draft-01.wsdl"
location="WS-BaseN.wsdl" />
<types>
<xsd:schema targetNamespace="http://example.com/satellite"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import
namespace="http://schemas.xmlsoap.org/ws/2004/03/addressing"
schemaLocation="WS-Addressing.xsd"/>
</xsd:schema>
</types>
<binding name="SubscriptionManagerSoapBinding"
type="wsnt:SubscriptionManager">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="SetResourceProperty">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="GetResourceProperty">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="Destroy">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="SetTerminationTime">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="PauseSubscription">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="ResumeSubscription">
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="SubscriptionManagerService">
<port name="SubscriptionManagerPort"
binding="tns:SubscriptionManagerSoapBinding">
<soap:address
location="http://example.com/satelliteSubscriptions" />
</port>
</service>
</definitions>
我们所需做的就是实现 SubscriptionManager portType,并且我们已经准备就绪。
除了 WS-Resource 所需的操作之外,SubscriptionManager 还必须实现 WS-Lifetime 中定义的操作,以便可以取消和重续订阅。还需要 PauseSubscription 和 ResumeSubscription 操作。
暂停订阅
既然我们已经设置好 SubscriptionManager,就来看一下它能做什么。假设管理员最近比较忙,想要暂停一会对 thresholdReached 主题的订阅。他应该发送一个 PauseSubscription 消息给 SubscriptionManager:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/PauseSubscription
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteSubscriptions
</wsa:To>
<sat:ResourceID>SUB3775</sat:ResourceID>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:PauseSubscription />
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
该消息发送到第一次创建订阅时返回的端点引用。假设一切运行良好,SubscriptionManager 会发送一个简单的 PauseSubscriptionResponse:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/PauseSubscriptionResponse
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteConsumer
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:PauseSubscriptionResponse />
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
当订阅暂停后,NotificationConsumer 就不会接收到任何消息。但是这对订阅的终止时间没有影响。一个月的订阅就持续一个月的时间,而不管暂停与否,除非改变了终止时间,如 取消或重续订阅 一节中所示。
恢复订阅
一旦管理员的工作负载降低了,他就决定恢复对 thresholdReached 主题的订阅。为此,他发送一个 ResumeSubscription 方法给 SubscriptionManager:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/ResumeSubscription
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteSubscriptions
</wsa:To>
<sat:ResourceID>SUB3775</sat:ResourceID>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:ResumeSubscription />
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
响应是一个简单的 ResumeSubscriptionResponse:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-BaseNotification-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsn/2004/06/WS-BaseNo
tification/ResumeSubscriptionResponse
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteConsumer
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsnt:PauseSubscriptionResponse />
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
订阅恢复时发生的事情取决于应用程序。NotificationProducer 可以选择重新发送所有错过的通知,重新发送错过的最后一个通知,或者什么也不做。
取消或重续订阅
Subscription 的生存期完全依赖于 WS-ResourceLifetime 规范中展示的概念。Subscription 开始具有一个 InitialTerminationTime,可以使用 SetTerminationTime 消息操纵这个时间。例如,管理员可以将他对 thresholdReached 主题的订阅设为在 2006 年 4 月 1 日愚人节那天到期:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsrl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsrf/2004/06/WS-Resou
rceLifetime/SetTerminationTime
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/satelliteSubscriptions
</wsa:To>
<sat:ResourceID>SUB3775</sat:ResourceID>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsrl:SetTerminationTime>
<wsrl:RequestedTerminationTime>
2006-04-01T00:00:00
</wsrl:RequestedTerminationTime>
</wsrl:SetTerminationTime>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
跟前面一样,如果一切运行良好,我们将会收到一个确认消息,其中包括 CurrentTime 和 NewTerminationTime:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sat="http://example.org/satelliteSystem"
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:wsrl="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd">
<SOAP-ENV:Header>
<wsa:Action>
http://docs.oasis-open.org/wsrf/2004/06/WS-Resour
ceLifetime/SetTerminationTimeResponse
</wsa:Action>
<wsa:To SOAP-ENV:mustUnderstand="1">
http://example.com/myClient
</wsa:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<wsrl:SetTerminationTimeResponse>
<wsrl:NewTerminationTime>
2006-04-01T00:00:00
</wsrl:NewTerminationTime>
<wsrl:CurrentTime>2005-12-15T04:37:36</wsrl:CurrentTime>
</wsrl:SetTerminationTimeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
我们也可以使用 WS-ResourceLifetime 马上取消订阅,方法是给它发送一个 Destroy 消息。
第六节:主题
TopicSpace
一旦做出了构建基于事件的系统的决定,就会存在大量不同类型的事件。这并不成问题,只要这些事件(或者是 WS-Notification 领域中的“情况”)的潜在消费者可以挑选出自己想要了解的。
在 WS-Notification 领域,这是通过使用主题 来处理的。每种情况对应一个主题,NotificationConsumer 可以挑选出他希望接收到通知的主题。
当然,主题并不存在于真空中。系统一般具有一组确定的主题,通过使用名称空间而区别于其他的组。一组通过名称空间而区别的主题叫做 TopicSpace。
发布者(我们当前的例子中叫做 NotificationProducer)可以选择支持哪个 TopicSpace,或者甚至是支持 TopicSpace 的哪些子集。发布者也可以支持多个用名称空间而区别的 TopicSpace 中的主题。
TopicSpace 由一个或多个 TopicTree 组成,TopicTree 是主题的层次。我们可以用 XML 来表示 topicSpace,但是与我们所想的稍微有点不同。考虑下面这个 topicSpace:
<wstop:topicSpace name="SatelliteTopics"
targetNamespace="http://example.com/satellite"
xmlns:sat="http://example.org/satellite"
xmlns:wstop="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-Topics-1.2-draft-01.xsd">
<wstop:topic name="Satellites">
<wstop:topic name="Counters">
<wstop:topic name="thresholdReached" />
<wstop:topic name="spike" />
<wstop:topic name="reset" />
</wstop:topic>
<wstop:topic name="SystemError">
<wstop:topic name="lockup" />
<wstop:topic name="burnup" />
<wstop:topic name="shootemup">
<wstop:topic name="alien" />
<wstop:topic name="terrestrial" />
</wstop:topic>
</wstop:topic>
<wstop:topic name="Communication">
<wstop:topic name="incoming" />
<wstop:topic name="outgoing" />
</wstop:topic>
</wstop:topic>
<wstop:topic name="Researchers">
<wstop:topic name="Active">
<wstop:topic name="ChocoMis" />
<wstop:topic name="SundaBab" />
<wstop:topic name="BrownRob" />
</wstop:topic>
<wstop:topic name="Inactive">
<wstop:topic name="GoddaRob" />
<wstop:topic name="VonBrWer" />
</wstop:topic>
</wstop:topic>
</wstop:topicSpace>
作为 topicSpace 元素的孩子的那些主题被认为是根主题,因为它们没有双亲。注意,这与 XML 根元素不同,XML 根元素是 topicSpace 元素。在本例中,我们有两个根主题:Satellites 和 Researchers。它们又各自有一个子主题,子主题又有自己的子主题。具有孩子的主题是 TopicTree 的头部。
主题属性
除了定义主题之外,我们还可以在某种程度上限制主题。例如,考虑下面这些修改:
<wstop:topicSpace name="SatelliteTopics"
targetNamespace="http://example.com/satellite"
xmlns:sat="http://example.org/satellite"
xmlns:wstop="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-Topics-1.2-draft-01.xsd">
<wstop:topic name="Satellites">
<wstop:topic name="Counters">
<wstop:topic name="thresholdReached" />
<wstop:topic name="spike" final="true" />
<wstop:topic name="reset"
messageTypes="sat:reboot sat:crash" />
</wstop:topic>
...
将主题标记为 final="true" 是告诉发布者,系统不能动态创建子主题。例如,根据该 topicSpace,用户可以请求订阅 thresholdReached/halfway 的主题,但不订阅 spike/reallyBig。
我们也可以限制可以为某个主题发送的消息的类型。例如,只可以为 reset 主题发送 sat:reboot 和 sat:crash 消息。如果没有指定 messageTypes 属性,则可以为主题发送任何消息。(我们也可以通过向主题添加一个 MessagePattern,按内容限制主题的消息,但是我们这里不必这样做。)
我们也可以创建是其他主题的别名的主题。
别名
要使 topicSpace 更完善,就要更加深入了解这样的情况,即有多个主题名称应用于同一情况。也许有拼写错误,也许我们想要创建一个“包罗万象的”主题。这些都是会出现别名的情况。
我们可以创建一个叫做 allRestarts 的主题,应用于 Satellites/Counters/reset 主题和 Satellites/SystemError 下的所有主题。用户可以简单地订阅 Satellites/Counters/allRestarts 主题,以接收所有这些通知:
<wstop:topicSpace name="SatelliteTopics"
targetNamespace="http://example.com/satellite"
xmlns:sat="http://example.org/satellite"
xmlns:wstop="http://docs.oasis-open.org/wsn/2004/06/wsn-WS-Topics-1.2-draft-01.xsd">
<wstop:topic name="Satellites">
<wstop:topic name="Counters">
<wstop:topic name="threshholdReached"
messageTypes="sat:thresh" />
<wstop:topic name="spike" />
<wstop:topic name="reset" />
<wstop:topic name="allRestarts">
<wstop:AliasRef
dialect="http://doc.oasis-open.org/wsn/2004/06/TopicExpression/FullPath">
sat:Satellites/sat:SystemError//* |
sat:Satellites/sat:Counters/sat:reset
</wstop:AliasRef>
</wstop:topic>
</wstop:topic>
<wstop:topic name="SystemError">
<wstop:topic name="lockup" />
<wstop:topic name="burnup" />
<wstop:topic name="shootemup">
<wstop:topic name="alien" />
<wstop:topic name="terrestrial" />
</wstop:topic>
</wstop:topic>
<wstop:topic name="Communication">...
表达式的实际形式依赖于所选择的 dialect。我们将在 指定主题:TopicExpression 一节中进一步谈论方言(dialect)。
NotificationProducer 主题支持
当然,主题并不是每个人都能随便使用,除非 NotificationConsumer 可以订阅它们,所以我们花点时间讨论一下 NotificationProducer 可以支持主题的方式。
生产者通过 topic 资源属性决定它将支持哪些主题。这些 topic 可以是简单的主题名称,或者可以是一个完全的表达式,选择一整棵主题树。多个 Topic 元素可以引用多个 topicSpace 中的主题。
生产者也可以决定是否支持主题的动态添加。如 主题属性 一节中所讨论的,如果主题未被标记为 final,那么消费者可以请求还未定义的子主题。但是,即使主题未被标记为 final,NotificationProducer 通过将它自己的 FixedTopicSet 资源属性设置为 true,可以阻止到它支持的主题集合的这类动态添加。
特别的 topicSpace
在有些情况下,我们可能无法预知需要支持哪些主题。例如,如果构建一个基于 WS-Notification 的允许用户输入搜索条目的搜索引擎,我们就不必预先定义 topicSpace。
在这种情况下,我们可以将这些主题放在特别的 topicSpace 中,它使用名称空间 http://docs.oasis-open.org/wsn/2004/06/TopicSpaces/adHoc。但是请记住,其他任何人使用特别的 topicSpace 也会使用该名称空间,所以显然存在命名冲突的潜在危险。
指定主题:TopicExpression
尽管我们可以选择任何喜欢的方案来指定主题 —— 这是 dialect 属性的美好之处 —— 但是 WS-Topics 规范推荐使用三个预定义的表达式类型之一:
http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple:顾名思义,这个类型的表达式是简单的。它允许我们按名称简单地指定根主题。例如,使用示例 topicSpace,我们可以指定主题 sat:Satellites。SimpleTopic 表达式不能引用子主题,所以它们实际上只在非常简单的 topicSpace 中有用。
http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Concrete:这个类型的表达式允许我们指定一个到主题的路径,但是与 SimplePath 表达式一样,只允许指定单个主题。例如,我们可以使用 ConcreteTopicPath 表达式来指定 sat:Satellites/sat:Counters/sat:spike 主题。
http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Full:该类型的表达式类似于 XPath 表达式,因此我们可以指定子孙主题,并且可以使用 * 通配符来选择多个主题。例如,我们可以将 sat:SystemError 主题的所有子孙指定为 sat:Satellites/sat:SystemError//*。但是,它是 XPath 的一个简化版本,所以我们不能使用谓词来过滤一组主题。
WS-Notification 和 WSRF
ResourceProperty 更改
我们首先讨论 WS-Notification 的原因在于它们在 WSRF 中的角色,所以它让我们看到所有事情是如何组合在一起的。
从 WS-ResourceProperties 开始,规范提供了一种当资源属性更改时通知 NotificationConsumer 的标准方式。
首先,WS-Resource 必须是 NotificationProducer,这意味着像我们的人造卫星例子一样,它必须实现所需的 NotificationProducer 操作,并包含所需的资源属性。
一个或多个这样的资源属性是 Topic 元素。WS-Resource 将要为之产生通知的每个资源属性都应该被列为主题。
当属性的值改变时,资源就发送 wsrp:ResourcePropertyValueChangeNotification 消息,如下所示:
<wsrp:ResourcePropertyValueChangeNotification>
<wsrp:OldValue>
<sat:altitude>47700</sat:altitude>
</wsrp:OldValue>
<wsrp:NewValue>
<sat:altitude>0</sat:altitude>
</wsrp:NewValue>
</wsrp:ResourcePropertyValueChangeNotification>
通知的销毁
NotificationConsumer 可能感兴趣的另一种情况是 WS-Resource(比如订阅)的销毁。在这种情况下,同样也充当 NotificationProducer 的 WS-Resource 必须支持 ResourceTermination 主题。
当 WS-Resource 被销毁时,它应该发送一个包含 TerminationNotification 元素的消息,如下所示:
<wsrl:TerminationNotification>
<wsrl:TerminationTime>
2005-02-16T04:47:52
</wsrl:TerminationTime>
<wsrl:TerminationReason>
User Request
</wsrl:TerminationReason>
</wsrl:TerminationNotification>
ServiceGroup 通知
用户也可以订阅这样的主题,即当 ServiceGroup 的成员发生变化时,该主题会提供通知。当向组中添加或删除成员时,订阅了 ServiceGroup 的 ServiceGroupModification 主题的用户会接到通知。这些通知为组和成员服务提供端点。例如, EntryAdditionNotification 可能类似于下面这样:
<wssg:EntryAdditionNotification>
<wssg:ServiceGroupEntryEPR>
<wsa:EndpointReference
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:sat="http://example.org/satelliteSystem">
<wsa:Address>http://example.com/satellite</wsa:Address>
<wsa:ReferenceProperties>
<sat:SatGroupEntryId>
SATGRPENTRY29003
</sat:SatGroupEntryId>
</wsa:ReferenceProperties>
</wsa:EndpointReference>
</wssg:ServiceGroupEntryEPR>
<wssg:MemberServiceEPR>
<wsa:EndpointReference
xmlns:wsa="http://www.w3.org/2005/02/addressing"
xmlns:sat="http://example.org/satelliteSystem">
<wsa:Address>http://example.com/satellite</wsa:Address>
<wsa:ReferenceProperties>
<sat:SatelliteId>SAT8557</sat:SatelliteId>
</wsa:ReferenceProperties>
</wsa:EndpointReference>
</wssg:MemberServiceEPR>
<wssg:Content>
<sat:approvedBy>BossManBing</sat:approvedBy>
</wssg:Content>
</wssg:EntryAdditionNotification>
该通知列出针对已经添加的 ServiceGroupEntry 的端点引用、实际的 WS-Resource 或服务,以及可选的 Content 元素。EntryRemovalNotification 与此类似,但是包含一个可选的 wssg:Reason 元素。
结束语
本教程讨论了 WS-Notification 系列规范,它使我们可以使用 Web 服务创建基于事件的系统。具体来说,这些通知与 WSRF 集成,因而提供一种提供事件通知的标准方式,诸如状态更改和资源销毁都是事件。
Author: orangelizq
email: orangelizq@163.com
posted on 2007-12-27 10:54
桔子汁 阅读(2972)
评论(0) 编辑 收藏 所属分类:
Web Service