If you are familiar with how the paradigms of pub/sub and asynchronous-notification map to web services you probably know that there are two competing specifications in this space, WS-BaseNotification and WS-Eventing. As a member of the WS-Notification "family", the former is an OASIS Standard while the later is, as of this writing, just a W3C Member Submission (now being developed by the W3C's Web Services Resource Access Working Group). The two specifications are very similar. They both define a set of operations whereby one party (the Subscriber) can subscribe to a series of asynchronous notification messages (sent in the form of SOAP messages) from another party (the Event Source) and thereafter manage that subscription. Both specifications leverage WS-Addressing for things like specifying where the notifications should be sent, creating unique references for managing multiple subscriptions, etc. However, there are places where the two specifications differ. One of these is WS-BN's use of "wrappers"; a generic XML element that acts as an envelope for the actual event information in the notification. Although WS-BaseNotification supports the use of "raw notifications", most of the specification deals with wrapped notifications. As I will show in the rest of this article, wrapped notifications are one of those ideas that, at first, seem worthwhile but which ultimately cause more problems than they solve.
Terminology
Another place where WS-BN and WS-Eventing differ is in their terminology. We need to pick one, so I'll flip a coin an go with WS-Eventing's. From Section 2.3 of WS-Eventing:
- Event Source - A Web service that sends Notifications and accepts requests to create subscriptions.
- Event Sink - A Web service that receives Notifications.
- Notification - A one-way message sent to indicate that an event has occurred.
- Subscriber - A Web service that sends requests to create, renew, and/or delete subscriptions.
- Subscription Manager - A Web service that accepts requests to manage get the status of, renew, and/or delete subscriptions on behalf of an event source.
The Case for Wrapping
The case for using wrapped Notifications rests on one or more of the following points.
- Wrapped Notifications support generic Event Sink listeners that can accept Notifications regardless of their type (i.e. XML structure). This allows a single listener to act as the Notification Endpoint for multiple subscriptions.
- Wrapped Notifications make it easier to implement things like brokers and store-and-forward queues that deal with Notifications in a generic way (i.e. where the structure and contents of the Notification are irrelevant).
- Wrapped Notifications are necessary if you want to work with dynamic Notification types who's structure may not be known at build-time.
Sample Wrapped Message
The following is an example of what a wrapped Notification might look like on the wire. I've invented a wrapper for WS-Eventing because we want to examine the differences between wrapped and unwrapped Notifications, not compare and contrast WS-BaseNotification and WS-Eventing.
Example Message 101 <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
02 xmlns:sc009="http://www.wstf.org/docs/scenarios/sc009"
03 xmlns:wsa="http://www.w3.org/2005/08/addressing"
04 xmlns:wse="http://www.w3.org/2009/02/eventing">
05 <soap:Header>
06 <wsa:MessageID>uuid:c58980ddc0a9010321162116d316bf43</wsa:MessageID>
07 <wsa:To>http://webservice.bea.com/POClient/notify12port</wsa:To>
08 <wsa:Action>http://www.w3.org/2009/02/eventing/NotifyEnv</wsa:Action>
09 </soap:Header>
10 <soap:Body>
11 <wse:NotifyEnv>
12 <sc009:OrderInfo xmlns:sc009="http://www.wstf.org/docs/scenarios/sc009">
13 <sc009:OrderID>sc009-order-5</sc009:OrderID>
14 <sc009:OrderDate>2008-11-11T21:32:36.718-05:00</sc009:OrderDate>
15 <sc0:OrderPrice>100</sc009:OrderPrice>
16 <sc009:OrderStatus>Approved</sc009:OrderStatus>
17 <sc009:LastUpdate>2008-11-11T21:33:01.765-05:00</sc009:LastUpdate>
18 </sc009:OrderInfo>
19 </wse:NotifyEnv>
20 </soap:Body>
21 </soap:Envelope>
The wrapper in the above example is the (fictional) wse:NotifyEnv element in lines 11-19. It contains the Notification information (the sc009:OrderInfo) 12-18). It should be readily apparent that what we are doing here is tunneling through SOAP; treating SOAP as transport mechanism and using it to carry another, higher-level envelope (the wse:NotifyEnv element).
Wrapper Schema and Generated Types
Now that we've had a look at what a wrapped message looks like on the wire, let's take a look at what the schema and WSDL for our wrapper might look like:
Example WSDL 1
01 <wsdl:definitions . . .>
02 <wsdl:types>
03 <xs:schema targetNamespace="http://www.w3.org/2009/01/eventing">
04 <xs:element name="NotifyEnv">
05 <xs:complexType mixed="true">
06 <xs:sequence>
07 <xs:any namespace="##any"
08 processContents="lax"
09 minOccurs="0"
10 maxOccurs="unbounded"/>
11 </xs:sequence>
12 </xs:complexType>
13 </xs:element>
14 </xs:schema>
15 </wsdl:types>
16 <wsdl:message name="NotifyEvent">
18 <wsdl:part name="body" element="wse:NotifyEnv"/>
19 </wsdl:message>
20 <wsdl:portType name="GenericSinkPortType">
21 <wsdl:operation name="NotifyEvent">
22 <wsdl:input message="wse:NotifyEvent"/>
23 </wsdl:operation>
24 </wsdl:portType>
25 . . .
26 </wsdl:definitions>
This seems straightforward enough, but let's look at the code that get's generated when we run this through a JAX-WS, WSDL-to-Java processor (I've elided the JAXB annotations for clarity):
Example Code 1
01 public class NotifyEnv {
02 protected List<Object> content;
03 public List<Object> getContent() {
04 if (content == null) {
05 content = new ArrayList<Object>();
06 }
07 return content;
08 }
09 }
Considering that this class derives from an XML element wrapped around a sequence of xs:anys, it shouldn't be surprising that all we have to work with is a collection of references to the java.lang.Object class. On the other hand, what the heck are we supposed to do with a list of Objects? It seems like we've lost something. Compare this with the code that is generated if we build our Event Sink from a WSDL that describes a raw Notification Interface like this one:
Example WSDL 2
01 <wsdl:definitions . . .>
02 <wsdl:types>
03 <xs:schema xmlns:sc009="http://www.wstf.org/docs/scenarios/sc009"
04 targetNamespace=http://www.wstf.org/docs/scenarios/sc009>
05 <xs:include schemaLocation="http://www.wstf.org/docs/scenarios/sc009/sc009.xsd"/>
06 </xs:schema>
07 </wsdl:types>
08 <wsdl:message name="NotifyPOStatus">
09 <wsdl:part name="part1" element="tns:OrderInfo"/>
10 </wsdl:message>
11 <wsdl:portType name="PONotifyPortType">
12 <wsdl:operation name="PONotify">
13 <wsdl:input message="tns:NotifyPOStatus"/>
14 </wsdl:operation>
15 </wsdl:portType>
16 . . .
17 </wsdl:definitions>
The above WSDL describes the interface that an Event Sink must implement if it subscribes to an Event Source that emits "NotifyPOStatus" Notifications. Note that the XML schema definition of the sc009:OrderInfo element used in line 9 can be found by de-referencing the schemaLocation attribute value on line 5. Here's the code that is generated:
Example Code 201 public class OrderInfoType {
02 protected String orderID;
03 protected XMLGregorianCalendar orderDate;
04 protected BigDecimal orderPrice;
05 protected String orderStatus;
06 protected XMLGregorianCalendar lastUpdate;
07 protected String orderComments;
08 . . .
09 public String getOrderID() {
10 return orderID;
11 }
12 public void setOrderID(String value) {
13 this.orderID = value;
14 }
15 public XMLGregorianCalendar getOrderDate() {
16 return orderDate;
17 }
18 . . .
19 }
Ok so, big deal; WSDL works. But that's just the point! In the wrapped case, there isn't anything for WSDL to work with. Because our NotifyEnv type must be able to wrap arbitrary XML ("xs:any"), there isn't any type information available to generate "fully featured" classes with getters and setters, etc. nor the marshalling code that converts to/from these classes and XML. From a WSDL perspective, wrapped notifications are weakly typed. Obviously we could write code that marshals and unmarshals to/from our notification data and XML. The "list of Objects" in Example Code 1 isn't really just a list of Objects. If we dug into it we might find that the objects were instances of some DOM class like ElementNSImpl, and we could certainly write code that parsed this DOM tree and built a useful class. But writing marshalling code is a time consuming and error prone task with little business value. To increase our efficiency, we might try something like XMLBeans to generate our marshalling and unmarshalling code. If we added some form of type metadata to our wrapped Notifications, the code that services the NotifyEvent operation could use that information to invoke the correct XMLBeans-generated parser. However, we still have to figure out some way to advertise what the schema types are for the set of possible Notifications that might result from a Subscription. It's not clear how long we would continue down this path before it occurred to us that what we were doing was inventing a "WSDL inside of WSDL" to go along with our "SOAP inside of SOAP". The long and short of it is that, by tunneling over SOAP, the use of wrapped Notifications has forced us to abandon our WSDL-based tools and invent another level of tools that do effectively the same thing.
The Case Against Wrapping
Earlier we laid out some of the arguments for why wrapped Notifications were necessary or at least a good idea. Let's go through them again, this time from an opposing view:
- Generic Listeners: It is unclear why anyone would want a generic Notification listener. Ultimately you need to dispatch the Notification message to some application code that will do something interesting with it. Most SOAP/HTTP stacks already have a generic listener that accepts HTTP requests and dispatches them to the appropriate message handling chain. Raw Notifications leverage this when the Event Sink creates an endpoint for accepting incoming Notifications. Duplicating this functionality at a higher layer is redundant.
- Brokers and Queues: While it is true that infrastructure components need to deal with Notifications in a generic way, it doesn't necessarily follow that the wrapper/envelope needs to be described in WSDL. The generic wrapper should be the SOAP envelope itself. It is possible to build a service that accepts arbitrary SOAP messages and re-publishes them, or persists them, etc. Building a broker this way is harder than building one from a WSDL-defined envelope but (a) there will be far more source and sink applications developed than there will be brokers and (b) vendors that should build brokers and queues, etc., not customers.
- Dynamic Notification Types: There are situations in which the structure and content of the Notification cannot be completely known at build-time, but it is seldom the case that the content of the Notification is completely arbitrary. Often there will be a known base with possible extensions. This situation can be modeled using traditional XML extension mechanisms. In cases where the content of the Notification is completely arbitrary it is difficult to imagine how you would write application logic that could effectively deal with this situation, regardless of how the data was transported.
Summary
The core of the case against wrapped Notifications lies in the answer to the following question "Why would I use SOAP-based technologies to implement a notification mechanism?" I assert that the answer has to be either (a) because I am invested in web services programming paradigms, tools, and runtimes and I want to continue to use these, and/or (b) because I am interested in interoperability between Event Sources and Event Sinks across different middleware implementations. In either case the value of a SOAP-based approach is diminished if we try to build your notification mechanism "on top of" SOAP rather than "within" SOAP. As I have shown, defining a wrapper message/operation in WSDL creates a situation in which we can't use WSDL to describe the application specific, notification types that are emitted by the Event Source. This means that we can't use our WSDL tools to generate language-specific binding classes and the code that marshals to and from the XML representations of those Notifications. This impacts both the efficiency and the interoperability of our notification infrastructure, which where the reasons we decided to use web services in the first place.
Comments (3)
One interesting concern related to unwrapped is that you are forced to make a choice between two uncomfortable options. Either you duplicate the expression of the topic for the notification as the root element of the notification payload (and we know that duplication is frowned upon);
Or you host it in a SOAP header. The SOAP header looks very inviting from a protocol perspective, however it feels awkward, if not inappropriate, to include headers that qualify the soap message in a disambiguating way. My interpretation is that we should reserve the header block for cross-cutting concerns (or at least tangential concerns.)
Instead of thinking of this as an "within" vs "on top of" debate, perhaps we should be asking "What is the message we are intending to exchange?" (for which SOAP is the protocol what will facilitate the exchange).
If the message is intended to be the notification, itself, then it is perfectly reasonable to have a wrapper ( and, maybe, we should just fix the tooling to account for Topic definition to be a viable part of the contract definition for notifications.)
Alternatively, if notification is less an intended message and more an interaction style, then unwrapped makes perfect sense. By this, I mean to say that one could argue the unwrapped camp believes that Notification is just a way to expand the consumer base for an existing message payload. This is in contrast to the wrapped camp which believes that Notification provides value-add services beyond simple message transfer.
Note: I find satisfaction looking at this from both perspectives, but wanted to assert that the wrapped argument is not without merit, and the unwrapped is not without detractors.
Posted by Baartz | July 6, 2009 8:16 AM
Posted on July 6, 2009 08:16
I'm not sure how "topics" factors into this discussion. "Topics", like "eventing", is one of those very broad terms that encompasses many different concepts. I'm not sure what definition of "topics" you are using, but I'm pretty sure it doesn't match mine. In my mind "topics" are a dynamic classification mechanism that is orthogonal to the type of the event (defined by it's schema) in the notification. Using the root element of the notification payload as a topic indicator completely defeats the purpose of using topics.
Also, I'm not sure I understand what you mean by "message is the notification" vs. "message is an interaction style". The "interaction style" is defined by the pub/sub paradigm; the Event Source transmits a one-way message (the Notification) to all the Event Sinks that are subscribed to that Event at the time the Event occurs. The definition I used in my article is "Notification - A one-way message sent to indicate that an event has occurred." The question is how you map the Event information into the SOAP message that is the Notification.
Posted by Gilbert Pilz | July 6, 2009 9:36 AM
Posted on July 6, 2009 09:36
Regarding topics, I find the boundary between Topic and Event to be less clear than you imply - and "orthogonal" was not my interpretation (although I'm eager to be found wrong - my ignorance on the subject is regularly exposed). My presumption was that events could be defined AS topics. The description of topic, at least as defined by WS-Topics, does little to indicate how deep a tree one should build. However, it appears reasonable to assert that the granularity should be sufficient to meet the differentiation necessary for subscribers, such that the preponderance of notifications they receive are interesting to them. The fact that subscription filtering is separated down the lines of topic and message content, and that topics appear to represent stable assertions about the context of the notifications, lead me to believe that all of the stable elements should be expressed in topics, leaving the messagecontent filter for those things that could not be expressed as topics.
I can certainly see the value in thinking of Topics as orthogonal, and this is consistent with my remark regarding "qualifying in a disambiguating way". This would imply that the event, as you describe, would be left to be communicated by the Notification message schema. I was presuming that if event identification was interesting, it would be articulated as a topic, itself.
One of the curious implications of making the event part of the payload is that, in order to have stability in the notify message schema, the schema must be reasonable for each event. For example, if the notifications are some sort of entity lifecycle, then one will need a schema that allows creation events to carry rich data and destruction events to only carry keys (there is no point in destruction events carrying full data). Invariably, this leads one to create a standardized structure to abstract the event specification from the meaty stuff. Then we are right back to square one, with some standard schema, embedded in a SOAP body, that defines an event representation and exposes an xsd:any child element. Can I presume that you would reject this, on the same grounds as your original argument?
Also, although it doesn't have a bearing on the relationships between topics and events, one problem with thinking of topics as completely orthogonal to the notification message is that WSN suggests that the topic can be sent along with the Notify message. Does its singular relationship to the Notify message suggest that there is a parent/child relationship between a given notify message and its topic? If it were truly orthogonal, then a given notify message may be bound to multiple topics found in different topic trees. Alternatively, I guess one could read into the spec that the Topic included in the Notify message is directly related to the subscription, but then I would not have expected the wsnt:Topic to be a peer of wsnt:SubscriptionReference.
Regarding the "interaction style" remarks, I was unclear. Obviously, the overarching paradigm for Notification is Pub/Sub. By interaction style, I was trying to differentiate between pub/sub of existing messages (where extending the consumer base is the motivation) and pub/sub of the notification). Again, this all harkens back to my expectation that events would be defined by the topic tree. This is relevant because, if the primary purpose of any messages being exchanged is the former, than unwrapped makes the most sense. So, if there was a transaction that exposed an entity creation service method, the notification payload would be the same message as the service method's.
In contrast, if the primary intent is to communicate the presence of the notice, rather than the content of the notice, then the payload's schema should focus on those characteristics. At some point, this is not a question of right or wrong, but a question of intent of the implementer.
Finally, and I recognize that this has drifted far off topic (pardon the pun), I now see the measured reason in including event context in the notification message. It ensures that the context is preserved under all circumstances, including detaching the notification message content for further processing.
Posted by Baartz | July 7, 2009 12:21 PM
Posted on July 7, 2009 12:21