Mixed-Language Programming


Working with SOAP, the Simple Object Access Protocol

Chris Dix

SOAP makes distributed computing possible in a multiplicity of forms, with XML as a communication medium. Little wonder it’s generating so much excitement.


Note: This article assumes that you are familiar with XML. For a refresher on XML syntax, see the sidebar, "The Components of XML."

Introduction

SOAP (Simple Object Access Protocol) is a communication protocol based on XML that simplifies the complexities of cross-platform and cross-language interaction. It is one of the cornerstones of Microsoft’s .NET strategy, but it is a standard that is also being supported by IBM and the Apache Software Foundation. The SOAP specification, currently on version 1.1, was originally created by developers at DevelopMentor, Microsoft, and UserLand Software. It has now been submitted to the W3C (World Wide Web Consortium) to allow the protocol to evolve as a standard. SOAP defines a message format, a set of rules for encoding data, a transport binding for HTTP, and a definition for representing RPC (remote procedure calls) using SOAP.

SOAP is likely to become the standard infrastructure for distributed applications because, as the name suggests, it keeps things simple. The authors of the SOAP specification have taken a practical approach to solving the difficulties that developers face when implementing distributed solutions. It does not impose any particular platform, language, library, or object technology on developers; SOAP implementations can be layered on top of any of these. It does not dictate a transport mechanism or what should be done after a message is processed. In this article, I will describe the fundamental aspects of SOAP and how it can be applied as a tool in a C++ application.

XML As Messages

SOAP messages are XML documents. In the SOAP model, these documents are transmitted to an endpoint for processing. The specification defines how this can be done using HTTP in a request-response pattern, but this is only one possible combination. For example, a SOAP message could be transmitted by SMTP to a server which would process it and then broadcast the response message to any number of other SOAP endpoints. SOAP messages can also be routed through a chain of endpoints for processing. This open model allows developers to choose transport protocols and communication patterns that best fit their requirements. As you might expect, SOAP messages are larger because they are XML, and this adds the overhead of parsing to the message processing. These are the disadvantages that come with using XML, but I believe that they are more than compensated for by the open and descriptive nature of the messages.

Unfortuntately, descriptive messages increase the likelihood that name collisions will occur. For example, your organization might define a SOAP message that passes employee information. When you are later called on to integrate your application with another organization using SOAP, you may find that you each have getEmployee messages that have little or nothing in common. This is a problem that has already been solved for XML, and the solution is namespaces [1]. Namespaces in XML serve the same function that they do in C++: they help resolve ambiguities in names.

In XML, namespaces are declared using attributes. Attributes that are named either xmlns or have xmlns: as a prefix are namespace declarations, and they associate an element (and its children) or a namespace prefix with a URI (Uniform Resource Identifier) [2]. It is the URI and not the prefix that is important. The attribute xmlns='some-uri' indicates that the default namespace of the element is 'some-uri', while the xmlns:x='some-uri' attribute indicates that any elements prefixed with x: are associated with the 'some-uri' namespace. The SOAP message format uses namespaces to identify and version elements, and it is good practice to use namespaces in your application-specific content as well in order to avoid ambiguities.

SOAP Message Format

The specification describes the format of a SOAP message in detail [3]. The text in Listing 1 shows an example of a SOAP message. There are three elements that make up a SOAP message: the Envelope, Header, and Body. The Envelope and the Body are both mandatory elements of a SOAP message, while the Header is optional.

The Envelope must be the first element of the message. Because the remainder of the message is wrapped by the Envelope, it is used to provide information about the version and serialization rules of the message. The version of SOAP being used is indicated by the namespace of the Envelope element. If you want your messages to be compliant with the current specification, the namespace URI of the Envelope must be 'http://schemas.xmlsoap.org/soap/envelope/'. If the Envelope has any other namespace URI, then a server application has to reject it. The serialization rules, or encoding style, of the message is indicated by the encodingStyle attribute of the Envelope. These are the rules that describe how types and values will be represented in the message. The rules defined in the specification are represented by the namespace URI 'http://schemas.xmlsoap.org/soap/encoding/'. If you need to follow some other standard for formatting data, you can use a different style. You can even elect to declare no style at all by providing a blank value for the attribute. Based on my experience, I believe that the encoding style that the specification defines will satisfy the needs of most developers because it was designed for RPC and thus uses familiar conventions.

The SOAP Header is optional and serves as a means of extending the SOAP message format. Elements can be placed in the Header to define additional valuable features such as encryption, transaction processing, or authentication; or you can add a Header element that meets the needs of your application. There are two attributes that define how applications must process an element in the Header. The first, actor, indicates the endpoint that the Header element is intended for. This allows messages to be passed through a chain of processing endpoints, each working only with the portion of the message that they need. The second, mustUnderstand, indicates that the processing of a Header element is mandatory. For example, a Header element containing a transaction ID might be marked as mustUnderstand to ensure that endpoints processing the message will consider it part of a transaction or reject it outright.

The Body element contains the actual contents of the message, and this is where you can place the data that your application needs to exchange. In the example in Listing 1 the Body contains a child element that describes a remote procedure call, but it could have contained any number of XML elements that the application required. When using SOAP messages as remote procedure calls, the specification defines how the Body should represent the call and response. A function call and response are each mapped to an element, and the children of that element are the parameters. The call element must be named just as the call itself is named, and the name of the response element is ignored. Parameter elements are named to correspond with the parameters. Only inbound parameters appear in the call, and only outbound parameters appear in the response. The specification also defines that the first child element of the response is the return value of the function. In Listing 1, the message is calling a function that maps to the C++ syntax below:

string DoCreditCheck( string ssn );

The Body in Listing 1 shows an example of how a namespace declaration can be used to better identify elements in the message. The prefix m: that appears on the DoCreditCheck element is there to indicate that this element is associated with the namespace URI 'http://no-duh.com/credit/'. This URI could have been any valid URI, but it is convenient to choose identifiers based on the URL of the service or schema. This lets the recipient of the message know that not only does this message contain a DoCreditCheck invocation, but it is the DoCreditCheck defined by no-duh.com. It is the responsibility of the SOAP endpoint to validate that the messages it receives correspond to the namespace that it expects.

There is one other case in which the structure of the Body is currently defined, and this is when the Body contains a Fault. The Fault element is the method defined by the specficiation for transmitting error information in a SOAP message, and it should appear only in messages that communicate error or status information. In the example request, the ssn parameter was invalid, so the server response contains a Fault describing the error. Listing 2 shows an example of such a response. The Fault element contains information about who generated the error, what type of error occurred, and the details of the error.

Binding SOAP to a Transport Protocol

Once you have composed a SOAP message, what do you do? Well, that sort of depends on the situation. SOAP messages can be transmitted in any fashion that the application requires. As long as the client and server have agreed upon a method, they can exchange SOAP messages. The specification only describes a single protocol binding, and that is for HTTP.

Listing 3 shows an example of a SOAP message sent using HTTP. The specification defines that requests should be sent using an HTTP POST. (The HTTP Extension Framework’s M-POST may also be used [4]). In the POST, the Content-Type header must be set to 'text/xml' to indicate that you are posting an XML document. There is also one special header for a SOAP HTTP POST: the SOAPAction header. The SOAPAction header is defined by the specification as a tool to help filter SOAP requests using firewalls. As of the 1.1 specification, this header is mandatory, but it can be blank if the address of the request is enough to identify the purpose of the message. With normal HTTP, the common pattern for message exchange is request-response, and it is no different for SOAP over HTTP. An example of an HTTP response to the previous request is provided in Listing 4.

Namespaces Revisited

I think it is useful to point out the differences in how namespaces are used in the example messages. Notice that the prefix for the namespace of the Envelope changes from 'SOAP' in Listing 1 to 'SOAP-ENV' in Listing 3. The prefix is irrelevant; it just needs to be consistent within the message; but the URI is important. After reading the specification, some people not familiar with namespaces might think that 'SOAP-ENV' is the required namespace prefix just because it is used throughout the document. Also, the namespace used by the Body contents appears on the Body element in Listing 3, while in Listing 1, the namespace is declared on the Envelope. Both of these are legal because namespace declarations apply to the element they appear on as well as its child elements. If you are using an XML parser to process your SOAP messages, these issues will probably be handled for you, but if you are parsing the message yourself, you should be aware of these types of issues.

Misconceptions

Since I began working with SOAP, I have found a couple of common misconceptions. First, SOAP is not XML-based remote procedure calls. Part of the specification deals specifically with how SOAP can be used to perform remote procedure calls, and the fact that SOAP shares a common history with XML-RPC clearly indicates that SOAP was built with this in mind [5]. While SOAP does work very well as a vehicle for RPC, the specification has grown beyond that. The format of the SOAP message is not inherently procedural. The latest version of the BizTalk Framework, Microsoft’s initiative for standardizing XML exchange for electronic commerce, uses SOAP messages to exchange business documents [6].

Second, SOAP is often described as being coupled with HTTP. It is true that the specification does describe how SOAP can be used with HTTP as the underlying transport protocol, but SOAP is not linked to any particular transport. SOAP is a higher level protocol than HTTP, more comparable to CORBA’s IIOP, and can use any number of transport protocols to transfer messages. Some work has been done in defining an SMTP binding for SOAP, and likely candidates for additional bindings include MSMQ (Microsoft Message Queue). The transport mechanism could even be as simple as writing a SOAP message to a file and dropping it into an "inbox" directory for processing.

Building a SOAP Client in C++

Now that I have mentioned what I believe are the common misconceptions, I can admit that much of the excitement surrounding SOAP is due to its ability to make RPC over HTTP simple. I have written a simple C++ client application that uses HTTP to exchange RPC messages with two different servers. Each time the application is executed it requests a stock quote from an example SOAP endpoint provided by Microsoft, and the results are sent to an endpoint at the SOAP-Web Services Resource Center. In order to view the results of the exchange, you can view the modified page at http://www.soap-wrc.com/webservices/defaultdemo.asp. The site requires a login for registered users, but for the purposes of the example I created a dummy user with a login of "cuj" and a password of "stroustrup". You can tell which items you generate because the text of those items will contain the host name of the machine the application was executed on.

The application uses some utility classes that I have written to simplify sending the SOAP message over HTTP. SimpleSoapRpcMessage is a class that builds a SOAP RPC message based on a given method name and parameter values. SoapHttpPostTransfer handles the details of SOAP message exchange for the HTTP protocol binding. An example of how these classes can be used is printed as Listing 5. These classes handle only simple XML (no CDATA sections or entity references). SimpleSoapRpcMessage can parse the return value from an RPC response message, but it cannot handle any out parameters other than the return value. The example messages in Listings 3 and 4 show the format of the request and response messages exchanged with the Microsoft endpoint.

In this example, we knew everything about the client, servers, and the messages they would exchange because we hardcoded it all. The classes I have written for this article can handle this simple XML without a parser, but in reality, most applications won’t always be receiving simple XML. I chose not to use an XML parser for this example in order to avoid a discussion on how to use a particular parser, but there are several free parsers available, including James Clark’s expat, the Microsoft MSXML parser, and Apache’s Xerces. Do not try to build a real C++ SOAP application without a parser. They are free, and they will make your life much easier.

Next Steps

SOAP is a relatively new technology, and there is room for it to grow. For instance, SOAP does not address several key areas of distributed systems, such as garbage collection and passing references to objects. SOAP as described by the 1.1 specification is a stateless protocol, so persistent object references are not possible without additional work. There are no facilities for transaction processing and no security beyond what the transport binding can provide. These are the types of services that really add value to a distributed architecture such as CORBA or DCOM. The specification provides a placeholder for these services in the SOAP Header element, and a standard format for these types of headers will hopefully be defined as vendors begin adding SOAP support to their tools. Because of the layer at which SOAP operates, DCOM or CORBA could possibly be built on top of SOAP and give developers the best of both worlds.

The SOAP specification does not define how servers will describe their services or how clients will discover those descriptions. For robust client and server implementations to be built, some sort of definition language and discovery method for SOAP services must be decided upon.

Recently there has been an attempt to resolve the issue of service description. WSDL (Web Services Description Language) is a joint proposal from Microsoft, IBM, and Ariba. It is a combination of the best of two earlier proposals (Microsoft’s SDL, the Service Description Language, and IBM’s NASSL, the Network Accessible Service Specification Language), and it provides an XML syntax for describing any number of Web Services, including those based on SOAP. The creation of WSDL is a very encouraging step for developers using SOAP because it will allow us to write tools and processes that can automatically determine the functionality a SOAP endpoint. WSDL syntax allows developers to define one or more SOAP message formats using schemas. Messages are then bound to one or more addresses that are SOAP endpoints. WSDL also allows these types of descriptions to be defined for protocols other than SOAP, such as standard HTTP POST. You can find the full WSDL specification at http://msdn.microsoft.com/xml/general/wsdl.asp. For additional SOAP resources, see the sidebar, “SOAP Resources.”

Finally, the SOAP reference implementations will continue to improve. Currently there are known issues with trying to make the Microsoft and IBM toolkits cooperate. These are not issues with the SOAP specification, just simple bugs that will get fleshed out over time. I mentioned earlier that SOAP applications are trivial when you know who your client and server are. Building generic, flexible SOAP clients and servers is far more complex, and it is important that Microsoft, IBM, Apache, and others collaborate when it comes to testing and to extending the protocol.

Conclusion

SOAP is a new technology that is going to help ease the burdens of distributed computing. It is likely that SOAP functionality will be built into the plumbing of every development tool, application server, and ORB in the near future. I have spent some time building tools to simplify SOAP development, and my experiences have convinced me that SOAP can be incorporated into applications today. Even though you may someday reap the benefits of SOAP without ever touching XML, it is still important to understand the core components and how you can use SOAP to solve problems today. Most developers have experienced the frustration of trying to find and debug problems with wizard-generated code. For many of us, SOAP-enabling wizards are right around the corner, and if we don’t understand the plumbing, we won’t be able to fix the leaks.

Acknowlegdements

I would like to thank both James Snell and Microsoft for providing the SOAP endpoints that I used in the example application. I would also like to thank Dave Winer, who answered several questions for me while I was working on this article.

References

[1] Various authors. "Namespaces in XML Recommendation," January 1999, http://www.w3.org/TR/REC-xml-names.

[2] Don Box, Aaron Skonnard, and John Lam. Essential XML: Beyond Markup (Addison-Wesley, 2000).

[3] Don Box et. al. "Simple Object Access Protocol (SOAP) 1.1," May 2000, http://www.w3.org/TR/SOAP/.

[4] H. Neilsen et. al. "HTTP Extension Framework," September 1999, http://www.w3.org/Protocols/HTTP/ietf-http-ext/draft-frystyk-http-extensions-03.

[5] Dave Winer, The XML-RPC Specification, http://www.xml-rpc.com/spec.

[6] Microsoft Corporation. "BizTalk Framework Specification 2.0," June 2000, http://msdn.microsoft.com/xml/articles/biztalk/biztalkfwv2draft.asp.

Chris Dix is a senior software developer for FMStrategies, a division of Little and Associates Architects. He has developed applications for Windows platforms using C++ for the past seven years, and he is currently building SOAP functionality into everything he can get his hands on. When he does have spare time, he spends it with his wife and two sons. He can be reached at soap4vb@carolina.rr.com.