XMLFrag header
Version 1.4
2007/5/25
This document defines an XML element designed to be used as a SOAP header. This header is used when interacting through SOAP with a service that presents an XML representation of a resource (such as the services that WS-Transfer operates on). This header allows access to fragment of the XML representation of the service. It also allows easy support of interaction with the service at different levels of granularity by consumers with different levels of knowledge of the model of the service.
Here is the XML representation of an example car fleet, accessible as a Web service:
<cf:carFleet xmlns:cf="http://example.com/models/carFleet"
xmlns="http://example.com/models/car">
<car>
<VIN>123456789</VIN>
<make>VW</make>
<model>Golf</model>
</car>
<car>
<VIN>234567891</VIN>
<make>Honda</make>
<model>Accord</model>
</car>
</cf:carFleet>
Let's assume that the EPR to access this carFleet service is:
<wsa:EndpointReference>
<wsa:Address>http://example.com/carFleet</wsa:Address>
</wsa:EndpointReference>
A WS-Transfer GET on this EPR would return the above <carFleet> element. The XMLFrag header introduced in this document is defined such that if the following header (you’ll recognize an XPath expression as the content of the header) was added to the SOAP message
<xf:XmlFrag xmlns:xf="http://vambenepe.com/XmlFrag">
/carFleet/car[VIN="123456789"]
</xf:XmlFrag>
the result of the GET request would now be:
<car xmlns="http://example.com/models/car">
<VIN>123456789</VIN>
<make>VW</make>
<model>Golf</model>
</car>
This has two benefits:
1) By using this header, the consumer of the carFleet EPR (who understands the carFleet model, i.e. both the http://example.com/models/carFleet and the http://example.com/models/car namespaces) now has a way to interact with fragments of the XML representation of the service rather than the entire representation.
2) If a consumer understands the model of a car but not the model of a carFleet, the consumer can interact with a car service (in this case the service represented the VW Golf) by being handed the following EPR:
<wsa:EndpointReference>
<wsa:Address>http://example.com/carFleet</Address>
<wsa:ReferenceParameters>
<xf:XmlFrag xmlns:xf="http://vambenepe.com/XmlFrag">
/carFleet/car[VIN="123456789"]
</xf:XmlFrag>
</was:ReferenceParameters>
</wsa:EndpointReference>
The consumer doesn't need to understand that the car service is in fact a fragment of a larger carFleet service. And the service doesn't need to understand that the consumer only deals with cars, not carFleets. Both of them can work at the level of the model (car or carFleet) that they want to deal with.
The difference between this mechanism and the wsman:FragmentTransfer header introduced by WS-Management is that wsman:FragmentTransfer only partially supports benefit (1) above and does not support benefit (2).
wsman:FragmentTransfer only partially supports (1) because it is limited to using messages defined by WS-Transfer on the fragment of the model while xf:XmlFrag does not restrict the SOAP messages, so one can use WS-Enumeration, WS-Eventing and really SOAP message as long as the semantic of the message make sense for that fragment of the model (e.g. "startEngine" makes sense on a car, not on a VIN).
Wsman:FragmentTransfer does not support (2) because, in addition to restricting the messages to WS-Transfer messages, wsman:FragmentTransfer also introduces an element called wsman:XmlFragment as a wrapper in the response. As a result, with WS-Management, a simple WS-Transfer GET on a car document does not return the same message as a WS-Transfer GET with a wsman:FragmentTransfer header pointing to a car on a carFleet document. With xf:XmlFrag, the two response messages are the same, so the EPR provided above can be used like any other EPR.
Here is the pseudo-schema for the XmlFrag element:
<xf:XmlFrag sequence="xs:uint"?>xs:string</xf:XmlFrag>
The value of this element is a string that contains an XPath statement that MUST resolve to a nodeset containing at most one node and if there is a node it must be an element node. This important restriction reduces the usefulness of this header for the purpose of accessing subsets of an XML document as it reduces the range of the possible subsets. But it allows the header to be used to provide transparent access to subsets of a model, which is key to allowing operations other than those defined by WS-Transfer and to allow this header to be used as a reference parameter. See section "Composition with wsman:FragmentTransfer or equivalent" for more on using this mechanism in conjunction with a mechanism such as wsman:FragmentTransfer to eliminate the limitation in the range of subsets of the XML document.
The @sequence attribute is used to provide an evaluation order of the XmlFrag header in case more than one such header is present. See section "Recursion" for more on this usage.
Let's assume that the XML representation of the carFleet service is a bit richer, such as:
<cf:carFleet xmlns:cf="http://example.com/models/carFleet"
xmlns="http://example.com/models/car">
<car>
<VIN>123456789</VIN>
<make>VW</make>
<model>Golf</model>
<e:engine xmlns:e="http://example.com/models/engine">
<e:cylinders>4</e:cylinders>
<e:gasType>unleaded</e:gasType>
</e:engine>
</car>
<car>
<VIN>234567891</VIN>
<make>Honda</make>
<model>Accord</model>
<e:engine xmlns:e="http://example.com/models/engine">
<e:cylinders>6</e:cylinders>
<e:gasType>unleaded</e:gasType>
</e:engine>
</car>
</cf:carFleet>
Putting all this information in one service is useful because a consumer that understands the entire model (carFleet, car and engine) and has access to the EPR for the service can run powerful queries, such as counting the number of cars in the fleet that use a certain type of gas. On the other hand, some consumers only understand a subset of the model (such as the car subset as we've seen above) and it is useful to allow these consumers to interact with such a service without having to understand the carFleet model and without putting any additional work on the service implementation either.
What if a consumer only needs to interact with the engine service for the engine of the VW Golf? Someone who understands the carFleet model and has access to the EPR for the carFleet service could create the necessary EPR for that consumer:
<wsa:EndpointReference>
<wsa:Address>http://example.com/carFleet</Address>
<wsa:ReferenceParameters>
<xf:XmlFrag xmlns:xf="http://vambenepe.com/XmlFrag">
/carFleet/car[VIN="123456789"]/engine
</xf:XmlFrag>
</was:ReferenceParameters>
</wsa:EndpointReference>
On the other hand, a consumer of the VW Golf car service would not be able to generate this EPR because generating it requires an understanding of the carFleet model. But it might be needed for a consumer of the VW Golf car service to generate an EPR to the engine service for that car (for example in order to allow a management agent to register for notifications on alerts from the engine). This is where the optional @sequence attribute is used. While the consumer of the car service cannot modify the XPath statement inside the car service EPR it was handled (because it might not understand this XPath statement), it can add an additional XmlFrag element to the reference parameters. The resulting EPR is:
<wsa:EndpointReference>
<wsa:Address>http://example.com/carFleet</Address>
<wsa:ReferenceParameters>
<xf:XmlFrag xmlns:xf="http://vambenepe.com/XmlFrag"
sequence="1">
/carFleet/car[VIN="123456789"]
</xf:XmlFrag>
<xf:XmlFrag xmlns:xf="http://vambenepe.com/XmlFrag"
sequence="2">
/engine
</xf:XmlFrag>
</was:ReferenceParameters>
</wsa:EndpointReference>
This EPR was derived from the car service EPR by following the rule of adding on the new XmlFrag element an attribute @sequence with a value equal to 1 more than the highest value of all @sequence attributes on the existing XmlFrag reference parameters. If there is only one such reference parameter existing in the EPR and it doesn't have a @sequence attribute, the new EPR should assign a @sequence attribute with a value of 1 to this existing reference parameter and one with a value of 2 to the new one. If the initial EPR doesn't have any XmlFrag reference parameter then no @sequence attribute is needed on the one added.
When receiving SOAP messages that contain XmlFrag SOAP headers, the service must process them in the order specified by the @sequence attributes. If there is more than one XmlFrag header and they don't all contain a @sequence attribute or if the @sequence attributes overlap then the service should fail the request with an IncorrectXmlFragSequence fault.
Note: alternative mechanisms that could be used instead of @sequence include creating a XmlFragBag element as the header and put all the XmlFrag elements inside of it (and relying on the order in which the XMLFrag elements appear to determine the order in which they should be processed). Another approach is to embed XmlFrag elements inside of one another as in:
<XmlFrag>
<Value>/carFleet/car[VIN="123456789"]</Value>
<XmlFrag><Value>/engine</Value></XmlFrag>
</XmlFrag>
How can such an EPR pointing to a subset of the XML representation of a service be used for operations other than those (like WS-Transfer) that only interact with the XML? Well, this is only a matter for the implementer of the service to use these EPRs for other operations. For example, if an engine service supports a "start" operation to start the engine, the implementation of the start operation can be written such that messages that contain the necessary XmlFrag header(s) to designate the specific engine will indeed start the specified engine. This doesn't mean that the start operation will succeed on any EPR built by adding XmlFrag reference parameters to the carFleet service EPR. Additionally, this doesn't prevent the start operation on that same engine from being also available through the use of another EPR. For example, the engine might have its own IP address the start operation could also be accessible through the following EPR:
<wsa:EndpointReference>
<wsa:Address>
http://car123456789.example.com/engine
</wsa:Address>
</wsa:EndpointReference>
The XmlFrag mechanism might result in EPRs being generated that work but are not optimal. In the car engine example, it might be a lot more effective to start and stop the engine by using the engine EPR that uses the network address of the engine (http://car123456789.example.com/engine) directly rather than by interacting with the carFleet service (at http://example.com/carFleet) which then has to somehow contact the engine to execute the request.
For these scenarios, we define a SOAP header called PreferredEPR (containing an EPR) that can be returned as part of the response to any message to instruct the consumer to use this EPR for follow-on interactions with the service. This header can be included as a response to a successful message as well as a response to a fault, depending on how much the service wants to force the consumer to switch EPR, versus simply suggesting the switch. We define a specific fault PreferredEPRUsageMandated used to signify that the consumer must use the preferred EPR and the requested should be repeated at the EPR included in the PreferredEPR to achieve the intended result. That fault could be returned for any use of the non-optimal EPR, or after a certain number of uses of it by the same requester, or only for certain messages, etc, at the discretion of the service.