Let's focus on the evolution of the Simple Object Access Protocol from its XML roots. In particular, I discuss how XML serves as the impetus for SOAP, and how its extensible nature can be used to effectively serialize data for distributed messaging. Let's begin by creating a simple XML messaging structure, then augment this example incrementally to illustrate how this generic XML messaging model can naturally evolve to conform to the SOAP specification, thereby punctuating the "Simple" in SOAP and "eXtensible" in XML.
Building Blocks
To begin the derivation of SOAP from its first principles, let's consider the following scenario. If an online merchant wishes to expose a method to place orders for an item, the retailer could use
a PlaceOrder method to accept relevant information from the client. In this instance, information such as the item name, item number, quantity, customer name, credit card type, and credit card number are all relevant to conducting the transaction. Once this information is processed and an order has been placed successfully, the method should return an order confirmation number for tracking purposes.
Such a method can be captured by the following prototype:
OrderNumber = PlaceOrder (ItemName, ItemNumber, Quantity, CustomerName, CreditCardType, CreditCardNumber)
If this method resides locally, consuming this method is a straightforward process. However, because this method resides on a remote server, a client can't simply issue a call to PlaceOrder to consume this method. Instead, the client must serialize the method call into a predetermined format, transport that serialized call across the network, and have the remote server deserialize the call, locate the remote object, and execute the method call. Once the call has been executed, the remote server must serialize the return value and send this result back to the client for interpretation. Figure 1 illustrates this process.
Figure 1
In addition to serializing the method call, there must be a means for transporting the call across the network. Although we could perform serialization and transport using a variety of mechanisms (DCOM and CORBA, for example, use their own specific wire and transport protocols), our objective is to use open, platform-neutral standards to realize this goal. So, let's serialize this method call using XML and transport the call across the network over one of the most pervasive transport protocols in use today: HTTP.
By serializing the call using XML and transporting it over HTTP, the request can be sent to a Web server listening for traffic on Port 80. A server-side script could act as a listener and dispatcher to interpret and parse the data stream and invoke the remote method.
One possible approach to serializing method calls using XML is to represent the method name as a parent element, and represent the parameters using child elements. Taking this approach, we can serialize the PlaceOrder method as follows:
<PlaceOrder>
<ItemName>Lightsaver</ItemName>
<ItemNumber>21382</ItemNumber>
<Quantity>2</Quantity>
<CustomerName>Luke Skywalker</CustomerName>
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
</PlaceOrder>
This serialization can be performed by a client proxy and sent to a remote endpoint using the HTTP POST method. On the server side, a stub will be required to handle the POST request, deserialize the XML stream, activate the requested object, and execute the method call (Figure 1). Once the requested method has been executed and the return value obtained, the server stub must serialize the response back to the client. One possible representation of the response could take the following form:
<PlaceOrderResponse>
<OrderNumber>10029645</OrderNumber>
þ Return Value
</PlaceOrderResponse>
In this case, we have created a parent node by concatenating the method name with "Response" and embedded the return value as a child of this node. While this isn't the only means of serializing the return value, it's a straightforward and effective format.
Although the above approach achieves the goal of serializing a method call in XML for remote execution, it is somewhat limited. For instance, some calls to a remote object may wish to provide additional processing information, such as the name of a second merchant to forward the request to, in the event that the requested item is out of stock. However, the above implementation by itself doesn't provide this capability because it's inappropriate to include this information with the method call. Fortunately, the extensible nature of XML makes it possible for us to augment the existing message structure to include additional details.
If we follow the approach formulated by HTTP requests, the client proxy could include additional information pertaining to routing, security, or debugging into a header, and separate the method call from this header. We can achieve this decoupling by wrapping the headers inside a <Header> element and encapsulating the method name inside another element such as <Body>. Since more than one parent node is now being added to the XML stream, we need an all-encompassing root element to ensure that the XML document is well formed. This root element can also be named arbitrarily, but since it envelops the entire XML structure, it is appropriate to call it the <Envelope> node. Our XML message structure now takes the form shown in Listing 1.
We have now successfully serialized the method call for remote execution by separating the method payload from processing details to provide us with additional flexibility. This flexibility is especially valuable in the Web services arena, whereby vendors and retailers can collaborate with each other to provide value-added services to customers.
Augmenting the Messaging Model
Since XML is extensible, the preceding serialization can be further modified to support compound data structures. For instance, instead of receiving the parameters ItemName, ItemNumber, Quantity, Cust-omerName, CreditCardType, and Credit-CardNumber as simple data types, the PlaceOrder method could receive compound types, which separate the item and customer details.
For example, details regarding the item to purchase can be encapsulated by the following data structure:
Struct ItemInfo
{
char* ItemName;
int ItemNumber;
int Quantity;
} ItemInfo;
Similarly, customer information can be captured in the following CustomerInfo structure:
Struct CustomerInfo
{
char* CustomerName;
char* CreditCardType;
int CreditCardNumber;
} CustomerInfo;
With the introduction of the ItemInfo and CustomerInfo structures, we need to update the method interface:
OrderNumber = PlaceOrder (ItemInfo, CustomerInfo)
The next step requires us to serialize this augmented method call in XML. Because of the extensible nature of XML, one means of achieving this serialization is to represent the compound type as a parent node and its embedded simple types as child nodes (see Listing 2).
We can now begin to see how the extensibility of XML can be used effectively to help us serialize richer data types.
Namespace Additions
Another point to consider in this XML serialization is that of namespace collisions. We should distinguish standard keywords we have defined above (Envelope, Header, Body) through a unique namespace qualification. Furthermore, it would be useful to distinguish our PlaceOrder method from other methods with the same name. Since XML supports namespaces, the above representation can be easily augmented to conform to the namespace format:
NamespaceIdentifier: ElementName
xmlns:NameSpaceIdentifier=URI
Let's start by namespace-qualifying our root element (Envelope) and all nodes directly below it (Body, Header). Since we are at liberty to define our namespace, let's qualify these keywords with the namespace identifier "SOAP-ENV" referenced by the URL http://schemas.xmlsoap.org/soap/envelope. In addition, we can identify our PlaceOrder method with the namespace identifier MyMethod, and the corresponding URL http://myserver.com. A similar format can be applied to the AddRoute header sub-element. Note that we only need to namespace-qualify the PlaceOrder method name and not the parameters, as the parameters are scoped to the method's namespace.
By incorporating the above namespace declarations, we have the XML serialization shown in Listing 3.
Take a close look at the serialized method call in Listing 3 because it represents a valid SOAP message. We have naturally progressed from taking a local method call, extending it to invoke a remote method, using XML to serialize the method call, and augmenting this message model to incorporate additional requirements. In the process, we have leveraged the flexibility of XML and observed how this extensibility can be used to arrive at a messaging model conforming to the SOAP specification.
Serializing Additional Complex Structures
Because of the extensibility of XML, we can further expand upon this messaging model to support additional requirements while still conforming to the SOAP specification.
For example, if a customer wishes to create an electronic wallet of payment methods, we can capture this requirement by extending our XML data structure within the SOAP message. Let's say the customer currently has three credit cards whose balances are all close to their maximum limit, and that he plans to apply for additional credit cards routinely. The interest he pays on these cards varies, so he would like to use his low interest card as much as possible. He'd prefer to avoid the hassle of checking balances on his card prior to each transaction, and would rather defer that decision to the online merchant. Furthermore, he'd like the flexibility of adding additional cards to his wallet dynamically and easily.
While there are many ways to represent this electronic wallet, for illustration purposes let's represent it as a singly linked list. That is, we want to create a linked list of credit card information, which is sent to a processing server, which begins at the head of the list (lowest interest credit card) and traverses the list until a credit card with a sufficient balance is found. Alternatively, the merchant server could split the bill among a number of low interest cards as a value-added service. (This type of service is another representation of Web services in action, with dynamic collaboration between a retail service and a credit card service).
We could represent this linked list as illustrated in Figure 2.
Figure 2
Based on this representation, each node of the linked list may be defined as follows:
typedef struct PaymentNode
{
char* CreditCardType;
int CreditCardNumber;
struct PaymentNode* pNextNode;
};
By defining this data structure to represent a list of credit cards, we can update the PlaceOrder method interface and define pHead as a pointer to the head of the list:
int PlaceOrder (ItemInfo, CustomerName, PaymentNode* pHead)
The next challenge is to serialize this linked list in XML. Using the extensible nature of XML, one way to achieve this goal is to use the ID attribute to uniquely reference each node of the linked list. Because we also need a means of identifying the head of the linked list, let's define an attribute called root to designate the first node in the list. Similarly, we can define an attribute called null to indicate the end of the list. Using these extensions, we could serialize our linked list in the following fashion:
Notice how the root attribute references the head of the list. This attribute is actually a valid SOAP construct (scoped to the SOAP-ENV namespace), and can be used by an element to broadcast itself as the root of a graph. The above serialization achieves our goal of capturing a linked list in XML, and we can incorporate it into our existing SOAP message, which now takes the following form:
Notice how the call in Listing 5 is serialized. A reference to the head of the list is created inside the body of the PlaceOrder method, but the list itself is defined outside the PlaceOrder method.
What About Datatype Binding?
Note that so far our datatypes have been serialized as invariants, without any type declarations on our part. We can, however, explicitly define our data types in one
of two ways. In both cases, we inherit simple types from the "XML Schemas Specification 2: Datatypes."
First, we could create an element schema to define types, in which case we achieve early binding. For example, we could define our ItemInfo and CustomerInfo data structures in an element schema as follows shown in Listing 6.
An alternative approach is to declare types in line with serialization using the xsi:type attribute. In this case, we achieve a late bound model, enabling a polymorphic element (accessor) to access values of several types at runtime. For example, we can take our first iteration of serializing PlaceOrder and bind each parameter inline to a type as seen in Listing 7.
Here, we have declared the basic types set forth by the XML Schemas Specification, and created instances of the type inline during serialization, to bind our elements to simple data types.
Next Steps
By progressing through a natural sequence, we have derived a complete SOAP messaging structure to conduct distributed messaging between a local client and a remote server. We began with a local method call and extended it to invoke a method on a remote server by serializing the call using XML. In the process, we observed how the extensibility of XML allowed us to augment our messaging model incrementally and arrive at the SOAP specification through a natural progression. We also saw how to expand upon this model to accommodate additional requirements and obtain greater flexibility. SOAP provides us with a simple, standardized means of conducting distributed messaging. We now have a better understanding of the basis for SOAP, and how its simplicity is derived from the extensible nature of XML.
Of course, SOAP provides many additional capabilities beyond what was described here, but many of those ideas can be derived from the first principles using the same exercises we have performed in this article.
I encourage you to formulate some of these ideas for yourself, as it will help you develop and extend your own SOAP applications. Keep in mind that the extensibility of XML must be handled with care; the extensible nature of XML can be a danger in and of itself, opening the door for a myriad of messaging structures to emerge. This is why standardization efforts such as SOAP are so important, to lay the groundwork for a well-understood and common messaging format.
Author Bio:
Ali Solehdin is a software engineer in Enterprise Architecture and Advanced Technologies at Infowave Software Inc., a leader in developing wireless applications for the enterprise. He has worked extensively in developing XML-based applications and infrastructures, particularly with SOAP, to develop distributed, firewall-friendly frameworks. Solehdin is a strong proponent of Web services, XML, and their family of related technologies. asolehdin@infowave.com
Deriving SOAP, by Ali Solehdin
WSJ Vol 01 Issue 01 - pg.33
Listing 1
<Envelope> þ Root element for well-formed XML
<Header>
<AddRoute>http://mysecondfavoriteretailer.com</AddRoute>
</Header>
<Body>
<PlaceOrder>
<ItemName>Lightsaver</ItemName>
<ItemNumber>21382</ItemNumber>
<Quantity>2</Quantity>
<CustomerName>Luke Skywalker</CustomerName>
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
</PlaceOrder>
</Body>
</Envelope>
Listing 2
<Envelope>
<Header>
<AddRoute>http://mysecondfavoriteretailer.com</AddRoute>
</Header>
<Body>
<PlaceOrder>
<ItemInfo> þ Compound Type
<ItemName>Lightsaver</ItemName>
<ItemNumber>21382</ItemNumber>
<Quantity>2</Quantity>
</ItemInfo>
<CustomerInfo> þ Compound Type
<CustomerName>Luke Skywalker</CustomerName>
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
</CustomerInfo>
</PlaceOrder>
</Body>
</Envelope>
Listing 3
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope">
<SOAP-ENV:Header>
<myRetailers:AddRetailer xmlns:myRetailers="http:// myserver.com/retailers">
http://www.mysecondfavoriteretailer.com
</myRetailers:AddRetailer>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<MyMethod:PlaceOrderxmlns:MyMethod="http://
myserver.com">
<ItemInfo>
<ItemName>Lightsaver</ItemName>
<ItemNumber>21382</ItemNumber>
<Quantity>2</Quantity>
</ItemInfo>
<CustomerInfo>
<CustomerName>Luke Skywalker</CustomerName>
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
</CustomerInfo>
</MyMethod:PlaceOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Listing 4
<pHead href="#Card1" /> Reference to head of list
<PaymentNode id="Card1" root="1"> Define head of list
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
<pNextNode href="#Card2" />
</PaymentNode>
<PaymentNode id="Card2">
<CreditCardType>MasterCard</CreditCardType>
<CreditCardNumber>0987654321</CreditCardNumber>
<pNextNode href="#Card3" />
</PaymentNode>
<PaymentNode id="Card3" />
<CreditCardType>AMEX</CreditCardType>
<CreditCardNumber>1122334455
</CreditCardNumber>
<pNextNode null="1" /> Designate last node
</PaymentNode>
Listing 5
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope">
<SOAP-ENV:Header>
<myRetailers:AddRetailerxmlns:myRetailers="http://
myserver.com/retailers">
http://www.mysecondfavoriteretailer.com
</myRetailers:AddRetailer>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<MyMethod:PlaceOrderxmlns:MyMethod="http://
myserver.com">
<ItemInfo>
<ItemName>Lightsaver</ItemName>
<ItemNumber>21382</ItemNumber>
<Quantity>2</Quantity>
</ItemInfo>
<CustomerName>Luke Skywalker</CustomerName>
<pHead href="#Card1" />
</MyMethod:PlaceOrder>
<MyMethod:PaymentNode id="Card1" SOAP-ENV:root="1">
<CreditCardType>Visa</CreditCardType>
<CreditCardNumber>1234567890</CreditCardNumber>
<pNextNode href="#Card2" />
</MyMethod:PaymentNode>
<MyMethod:PaymentNode id="Card2">
<CreditCardType>MasterCard</CreditCardType>
<CreditCardNumber>0987654321</CreditCardNumber>
<pNextNode href="#Card3" />
</MyMethod:PaymentNode>
<MyMethod:PaymentNode id="Card3">
<CreditCardType>AMEX</CreditCardType>
<CreditCardNumber>1122334455</CreditCardNumber>
<pNextNode null="1"/>
</MyMethod:PaymentNode>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Listing 6
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">
<xsd:element name="ItemName" type="xsd:string"/>
<xsd:element name="ItemNumber" type="xsd:int"/>
<xsd:element name="Quantity" type="xsd:int"/>
<xsd:element name="CustomerName" type="xsd:string"/>
<xsd:element name="CreditCardType" type="xsd:string"/>
<xsd:element name="CreditCardNumber" type="xsd:int"/>
<xsd:complexType name="ItemInfo">
<xsd:sequence>
<xsd:element ref="ItemName"/>
<xsd:element ref="ItemNumber"/>
<xsd:element ref="Quantity"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CustomerInfo">
<xsd:sequence>
<xsd:element ref="CustomerName"/>
<xsd:element ref="CreditCardType"/>
<xsd:element ref="CreditCardNumber"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Listing 7
<SOAP-ENV:Envelope xmlns:SOAPENV="http://
schemas.xmlsoap.org/soap/envelope"
xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-Instance">
<SOAP-ENV:Header>
<myRetailers:AddRetailerxmlns:myRetailers="http://
myserver.com/retailers">
http://www.mysecondfavoriteretailer.com
</myRetailers:AddRetailer>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<MyMethod:PlaceOrderxmlns:MyMethod="http://
myserver.com">
<ItemName xsi:type="xsd:string">Lightsaver</ItemName>
<ItemNumber xsi:type="xsd:int">21382</ItemNumber>
<Quantity xsi:type="xsd:int">2</Quantity>
<CustomerName xsi:type="xsd:string">Luke Skywalker</CustomerName>
<CreditCardTypexsi:type="xsd:string">Visa</CreditCardType>
<CreditCardNumberxsi:type="xsd:int">1234567890
</CreditCardNumber>
</MyMethod:PlaceOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>