Playing Together Nicely: Getting REST and SOAP to Share Each Other's Toysby Jason R. Briggs
It's tremendously difficult to argue a RESTful approach to a service-oriented architecture (SOA), when the corporate mindshare is SOAP--where project stakeholders tout the SOA buzzword, nod their heads sagely when you say SOAP, nod their heads again when you say XML-RPC, and then look blankly when you mention REST. At an official level, it seems that for the IBMs, Suns, Microsofts, and Oracles (et al) of this world, REST isn't even on the radar; perhaps more because they would find it difficult to build a commercial strategy around something that is based on simplicity and standards (like HTTP) that have been around for years, than from a true lack of visibility at the coalface. So when the push from on high is for SOAP web services and associated technologies, and your business partners and colleagues have been drinking the Sun/MS/IBM/etc. Kool-Aid, you're generally fighting a losing battle if you're promoting alternatives.
While you might usually end up stuck in a buzzword-compliance nightmare, with packets of WSDLs, BPELs, and SOAPs flying around left, right, and center, there are occasions where it may be possible to push through a REST-style, resource-centric approach; where there is no official strategic direction for SOA already in place, where there is a reasonable amount of flexibility and imagination on the part of the project owners, and perhaps with a bit of technical enlightening on the part of the technical lead(s).
But it does still come back to the fact that a service-oriented architecture, in particular, is not a simple, disconnected, independent application, and that many of the organizations wishing to interact with the system will be expecting the (in some cases, de facto) standard mechanisms for interaction, and won't be happy unless your software is emitting buzzwords and acronyms like an engine with a split gasket spits oil.
The best method I've come up with so far to get around this issue is to deliver both. Design the core of your system with REST resources, and subsequently plug in some kind of interfacing or adapter to the REST components, in order to deliver SOAP messaging for those who require it. This article discusses one approach to a SOAP interface for REST services. However, one thing we aren't going to do is resolve issues of context between REST's coarser-grained idea of a resource, and SOAP's fine-grained RPC methods. There is guaranteed to be a significant chunk of the REST crowd who think, "Why bother? A REST resource doesn't map to SOAP calls and it shouldn't, anyway." That's fine, and agreed, to a point--however, this article is aimed at those fighting the good fight in corporate IT, wanting to design REST systems, but stuck delivering SOAP messages because it's the standard, and what everyone expects you to deliver.
A side note: there are, of course, SOAP web methods, which provide an HTTP verb-style interface to a standard SOAP web service--which would be a more direct mapping to a REST resource. But if you're using a form of orchestration, pulling a number of services and processes together with something like BPEL, you are potentially going to be constrained (at the very least by convention, but perhaps also by technical considerations of the orchestration and choreography tools) to using
GET and (more commonly)
POST for your SOAP methods.
The REST Resource Definition
The place I'll start is with the REST services, or resources (in REST terminology). For the purposes of this discussion, I've (RAD) prototyped all of the code in Jython, which gives me access to all of the Java libraries I need to use, and also allows me to throw components together without too much infrastructure (and without too much in the way of recompilation and preparation). Jython also has the advantage of being able to (generally) do more in less space than Java, so from the point of view of explanation, it's quite a useful tool. That said, it's worthwhile to note that in a production setting, I would re-implement certain segments of the code, where performance would be an issue (the interfacing specifically), in "native" Java, due to Jython's performance limitations.
To get the directory structure out of the way first, I've set up a directory in my servlet container's webapps directory with the basic layout in Figure 1.
Figure 1. Directory layout
Working servlet files (in this case, Jython scripts) will be placed in the root test folder, while source code obviously goes in the source directory and is compiled to classes--pretty much as you might expect. I'll come to the generated directory shortly.
To test the services for yourself, extract the contents of the sample code rest+soap.zip file, found in the Resources section, into the webapps directory of your servlet container (tested on Jetty and Tomcat 5). The various .jar files required for WEB-INF/lib are detailed in the readme.txt file located in that directory. Source code can be compiled using Ant and the build.xml file located in source.
For this example, my REST service will be a "shopping basket" of stocks, supporting
GET (retrieve an existing shopping basket),
PUT (create or update a shopping basket), and
DELETE (remove a basket). The URI for accessing the resource will be http://localhost:8080/test/basket/<basket_name>, so it could be viewed from a browser with something like http://localhost:8080/test/basket/mybasket. In addition, I'm going to use XML schemas to define the messages passed to and from my resource, and JAXB to simplify the process of conversion from flat XML to object. So the first step in this process will actually be defining the XML schema for the basket resource. In my (perhaps less than) infinite wisdom, I've made the decision that the definitions for my resource will go in schema file Basket.xsd, while any commonalities (i.e., definitions that could be used by other resources) will go in Common.xsd. For example, the definition for the
BasketRQ element is show below:
<xs:element name="BasketRQ"> <xs:annotation> <xs:documentation>a basket of stocks</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="Reference" type="Reference" minOccurs="0" /> <xs:element name="Items" minOccurs="0" maxOccurs="unbounded"> <xs:annotation> <xs:documentation>an item in the basket</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="Code" type="xs:string" /> <xs:element name="Quantity" type="xs:positiveInteger" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element>
BasketRQ is used for creating a basket of stocks--a basket can contain a reference, and then a number of items (each of which contain a string code and an integer quantity). Note the emphasis on can contain a reference--in the case of my REST resources, the reference won't feature in the XML message, but will be important when we get to the SOAP interface later. Including the
Reference element in this schema (despite the fact it isn't required at all by REST resources) is necessary to reduce the complexity of maintaining an alternate XML schema specially for the matching SOAP services.
The next schema excerpt shows the basket response (
BasketRS), which has two additional elements: the stock price, and total price after multiplying stock price by the quantity:
<xs:element name="BasketRS"> <xs:annotation> <xs:documentation>a basket of stocks</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="Reference" type="Reference" minOccurs="0" /> <xs:element name="Items" minOccurs="0" maxOccurs="unbounded"> <xs:annotation> <xs:documentation>an item in the basket</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="Code" type="xs:string" /> <xs:element name="Quantity" type="xs:positiveInteger" /> <xs:element name="Price" type="Money" /> <xs:element name="Total" type="Money" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element>
These two elements are used in the
PUT methods of the basket resource: a
PUT expects a
BasketRQ, while a
GET returns the
BasketRS. The elements (along with all other elements defined in these schema files) are generated as JavaBeans using a JAXB Ant task (which can be found in build.xml, again located in the source directory).