Chapter 10. The Resource-Oriented Architecture Versus Big Web Services
Throughout this book I’ve focused on technologies and architectures that work with the grain of the Web. I’ve shown you how to arrange resources into services that are very powerful, but conceptually simple and accessible from a wide variety of standard clients. But I’ve hardly said a word about the technologies that most people think of when they think web services: SOAP, WSDL, and the WS-* stack. These technologies form a competing paradigm for web services: one that doesn’t really work like the Web. Rather than letting these technologies claim the entire field of web services for themselves, I’ve given them a not entirely kind, but fairly mild nickname: Big Web Services. In this chapter I’ll compare the two paradigms.
The web is based on resources, but Big Web Services don’t expose resources. The Web is based on URIs and links, but a typical Big Web Service exposes one URI and zero links. The Web is based on HTTP, and Big Web Services hardly use HTTP’s features at all. This isn’t academic hair-splitting, because it means Big Web Services don’t get the benefits of resource-oriented web services. They’re not addressable, cacheable, or well connected, and they don’t respect any uniform interface. (Many of them are stateless, though.) They’re opaque, and understanding one doesn’t help you understand the next one. In practice, they also tend to have interoperability problems when serving a variety of clients.
In this chapter I apply the same analytical tools to Big Web Services as I’ve been using to explain the REST architectural style and the Resource-Oriented Architecture. I’ll be covering a lot of ideas in just a few pages—there are already whole books about these technologies—but my goal is not to give you a complete introduction. I just want to show you how the two philosophies line up. I’ll examine technologies like SOAP on a technical level, and not in terms of how they’ve been hyped or demonized. I’ll focus on these specifications as they’re widely deployed, and less on the unrealized potential of newer versions.
The vision of Big Web Services has evolved over time and not all practitioners are up to date on the latest concepts. To make sure I get everybody, I’m going to take a chronological approach to my analysis. I’ll start with the original “publish, find, and bind” vision, move on to “secure, reliable transactions,” and finally touch on more recent developments like the Enterprise Server Bus, Business Process Execution Language, and Service-Oriented Architecture.
What Problems Are Big Web Services Trying to Solve?
As I said, the Web is resource-oriented. To implement the RPC style atop it is to go against the grain of the Web. But the Web wasn’t designed to support general-purpose distributed programming. Sometimes your application has a natural grain of its own, and going against that is problematic.
Here’s a concrete example that I’ll come back to throughout this chapter: a service that sets up travel reservations. Booking a trip might require booking a flight, a hotel, and a rental car. These tasks are interrelated: getting a rental car and a seat on a flight may be of little use to the client if you can’t find a hotel. Each task requires coordinating with external authorities to find the best deal: the airlines, the rental car companies, the hotels. Each of these external authorities may be a separate service, and dealing with them involves making commitments. You may be able to have the airline service hold a seat on a plane for five minutes while you try to line up the rest of the deal. You may need to make a hotel reservation that will bill the customer for the first night’s stay whether or not they show up. These time-limited commitments represent shared state.
The resource-oriented approach I advocate in this book is Turing-complete. It can model any application, even a complex one like a travel broker. If I implemented this travel broker as a set of resource-oriented services, I’d expose resources like “a five-minute hold on seat 24C.” This would work, but there’s probably little value in that kind of resource. I don’t pretend to know what emergent properties might show up in a resource-oriented system like this, but it’s not likely that someone would want to bookmark that resource’s URI and pass it around.
The travel agency service has a different grain than the rest of the Web. This doesn’t mean that it can’t be made into a successful web application. Nor does it imply that SOAP and related specifications are a better fit. But this is the main problem that Big Web Services are trying to solve: the design of process-oriented, brokered distributed services. For whatever reason, this kind of application tends to be more prevalent in businesses and government applications, and less prevalent in technical and academic areas.
SOAP
SOAP is the foundation on which the plethora of WS-* specifications is built. Despite the hype and antihype it’s been subjected to, there’s amazingly little to this specification. You can take any XML document (so long as it doesn’t have a DOCTYPE or processing instructions), wrap it in two little XML elements, and you have a valid SOAP document. For best results, though, the document’s root element should be in a namespace.
Here’s an XML document:
<hello-world xmns="http://example.com"/>
Here’s the same document, wrapped in a SOAP envelope:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <hello-world xmns="http://example.com"/> </soap:Body> </soap:Envelope>
The only catch is that the SOAP Envelope
must have the same character encoding
as the document it encloses. That’s pretty much all there is to it.
Wrapping an XML document in two extra elements is certainly not an
unreasonable or onerous task, but it doesn’t exactly solve all the
world’s problems either.
Seem too simple? Here’s a real-world example. In Example 1-8 I showed you an elided version of a SOAP document you might submit to Google’s web search service. Example 10-1 shows the whole document.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <gs:doGoogleSearch xmlns:gs="urn:GoogleSearch"> <key>00000000000000000000000000000000</key> <q>REST book</q> <start>0</start> <maxResults>10</maxResults> <filter>true</filter> <restrict/> <safeSearch>false</safeSearch> <lr/> <ie>latin1</ie> <oe>latin1</oe> </gs:doGoogleSearch> </soap:Body> </soap:Envelope>
This document describes a Call to the Remote Procedure gs:doGoogleSearch
. All
of the query parameters are neatly tucked into named elements. This
example is fully functional,
though if you POST it to Google you’ll get back a fault document saying
that the key
is not valid.
This style of encoding parameters to a remote function is sometimes called RPC/literal or Section 5 encoding. That’s the section in the SOAP 1.1 specification that shows how to use SOAP for RPC. But over time, fashions change. Later versions of the specification made support of this encoding optional, and so it’s now effectively deprecated. It was largely replaced by an encoding called document/literal, and then by wrapped document/literal. Wrapped document/literal looks largely the same as section 5 encoding, except that the parameters tend to be scoped to a namespace.
One final note about body elements: the parameters may be
annotated with data type information based on XML Schema Data Types.
This annotation goes into attributes, and generally reduces the
readability of the document. Instead of <ie>latin1</ie>
you might see
<ie
xsi:type="xsd:string">latin1</ie>
. Multiply that by
the number of arguments in Example 10-1 and
you may start to see why many recoil in horror when they hear
“SOAP.”
In Chapter 1 I said that HTTP and SOAP
are just different ways of putting messages in envelopes. HTTP’s main
moving parts are the entity-body and the headers. With a SOAP element
named Body
, you might expect to also
find a Header
element. You’d be
right. Anything that can go into the Body
element—any namespaced document which has
no DOCTYPE or processing instructions—can go into the Header
. But while you tend to only find a
single element inside the Body
, the
Header
can contain any number of
elements. Header
elements also tend
to be small.
Recalling the terminology used in HTTP: Documents in Envelopes” in Chapter 1, headers are like “stickers” on an envelope. SOAP headers tend to contain information about the data in the body, such as security and routing information. The same is true of HTTP headers.
SOAP defines two attributes for header entities: actor
and mustUnderstand
. If you know in advance that
your message is going to pass through intermediaries on the way to its
destination, you can identify (via a URI) the actor
that’s the target of any particular
header. The mustUnderstand
attribute
is used to impose restrictions on those intermediaries (or on the final
destination). If the actor
doesn’t
understand a header addressed to it, and mustUnderstand
is true, it must reject the
message—even if it thinks it could handle the message otherwise. An
example of this would be a header associated with a two-phase commit
operation. If the destination doesn’t understand two-phase commit, you
don’t want the operation to proceed.
Beyond that, there isn’t much to SOAP. Requests and responses have
the same format, similar to HTTP. There’s a separate format for a SOAP
Fault
, used to signify an error
condition. Right now the only thing that can go into a SOAP document is
an XML document. There have been a few attempts to define mechanisms for
attaching binary data to messages, but no clear winner has
emerged.
Given this fairly simple protocol, what’s the basis for the hype and controversy? SOAP is mainly infamous for the technologies built on top of it, and I’ll cover those next. It does have one alleged benefit of its own: transport independence. The headers are inside the message, which means they’re independent of the protocol used to transport the message. You don’t have to send a SOAP envelope inside an HTTP envelope. You can send it over email, instant messaging, raw TCP, or any other protocol. In practice, this feature is rarely used. There’s been some limited public use of SMTP transports, and some use of JMS transports behind the corporate firewall, but the overwhelming majority of SOAP traffic is over HTTP.
The Resource-Oriented Alternative
SOAP is almost always sent over HTTP, but SOAP toolkits make little use of HTTP status codes, and tend to coerce all operations into POST methods. This is not technically disallowed by the REST architectural style, but it’s a degenerate sort of RESTful architecture that doesn’t get any of the benefits REST is supposed to provide. Most SOAP services support multiple operations on diverse data, all mediated through POST on a single URI. This isn’t resource-oriented: it’s RPC-style.
The single most important change you can make is to split your service into resources: identify every “thing” in your service with a separate URI. Pretty much every SOAP toolkit in existence provides access to this information, so use it! Put the object reference up front. Such usages may not feel idiomatic at first, but if you stop and think about it, this is what you’d expect to be doing if SOAP were really a Simple Object Access Protocol. It’s the difference between object-oriented programming in a function-oriented language like C:
my_function(object, argument);
and in an object-oriented language like C++:
object->my_method(argument);
When you move the scoping information outside the parentheses
(or, in this case, the Envelope
),
you’ll soon find yourself identifying large numbers of resources with
common functionality. You’ll want to refactor your logic to exploit
these commonalities.
The next most important change has to do with the
object-oriented concept of polymorphism. You should try to make
objects of different types respond to method calls with the same name.
In the world of the Web, this means (at a minimum) supporting HTTP’s
GET
method. Why is this
important? Think about a programming language’s standard library.
Pretty much every object-oriented language defines a standard class
hierarchy, and at its root you find an Object
class which defines a toString
method. The details are different
for every language, but the result is always the same: every object
has a method that provides a canonical representation of the object.
The GET
method provides a similar
function for HTTP resources.
Once you do this, you’ll inevitably notice that the GET
method is used more heavily than all
the other methods you have provided. Combined. And by a wide margin.
That’s where conditional GET and caching come in. Implement these
standard features of HTTP, make your representations cacheable, and
you make your application more scalable. That has direct and tangible
economic benefits.
Once you’ve done these three simple things, you may find yourself wanting more. Chapter 8 is full of advice on these topics.
WSDL
SOAP provides an envelope for your messages, but little else. Beyond section 5 (which is falling out of favor), SOAP doesn’t constrain the structure of the message one bit. Many environments, especially those that depend on static typing, need a bit more definition up front. That’s where WSDL comes in.
I’m going to illustrate the concepts behind WSDL using the weblogs.com
SOAP 1.1 interface. I
chose it because it’s pretty much the simplest deployed SOAP interface
out there. Any service you encounter or write will undoubtedly be more
complicated, but the basic steps are the same.
The weblogs.com
interface
exposes a single RPC-style function called ping
. The function takes two arguments, both
strings, and returns a pingResult
structure. This custom structure contains two elements: flerror
, a Boolean, and message
, a string. Strings and Booleans are
standard primitive data types, but to use a pingResult
I need to define it as an XML
Schema complexType
. I’ll do this
within the types
element of my WSDL
file in Example 10-2.
<types> <s:schema targetNamespace="uri:weblogscom"> <s:complexType name="pingResult"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="flerror" type="s:boolean"/> <s:element minOccurs="1" maxOccurs="1" name="message" type="s:string" /> </s:sequence> </s:complexType> </s:schema> </types>
Now that I’ve defined the custom type, I’ll move on to defining
the messages that can be sent between client and server. There are two
messages here: the ping
request and
the ping
response. The request has
two parts, and the response has only one (see Example 10-3).
<message name="pingRequest"> <part name="weblogname" type="s:string"/> <part name="weblogurl" type="s:string"/> </message> <message name="pingResponse"> <part name="result" type="tns:pingResult"/> </message>
Now I can use these messages in the definition of an WSDL
operation, which is defined as a part of a
port type. A port is
simply a collection of operations. A programming language would refer to
this as a library, a module, or a class. But this is the world of
messaging, so the connection points are called ports, and an abstract
definition of a port is called a port type. In this case, I’m defining a
port type that supports a single operation: ping
(see Example 10-4).
<portType name="pingPort"> <operation name="ping"> <input message="tns:pingRequest"/> <output message="tns:pingResponse"/> </operation> </portType>
At this point, the definition is still abstract. There are any
number of ways to implement this ping
operation that takes two strings and
returns a struct. I haven’t specified that the message is going to be
transported via SOAP, or even that the message is going to be XML.
Vendors of WSDL implementations are free to support other transports,
but in Example 10-5, the intended
binding is to section 5 compliant SOAP messages,
sent over HTTP. This is the SOAP/HTTP binding for the port type, which
will be presented without commentary.
<binding name="pingSoap" type="tns:pingPort"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="ping"> <soap:operation soapAction="/weblogUpdates" style="rpc"/> <input> <soap:body use="encoded" namespace="uri:weblogscom" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" namespace="uri:weblogscom" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding>
We’re still not done. The final piece to the puzzle is to define a service, which connects a portType with a binding and (since this is SOAP over HTTP) with an endpoint URI (see Example 10-6).
<service name="weblogscom"> <document> For a complete description of this service, go to the following URL: http://www.soapware.org/weblogsCom </document> <port name="pingPort" binding="tns:pingSoap"> <soap:address location="http://rpc.weblogs.com:80/"/> </port> </service>
The full WSDL for this single-function service is shown in Example 10-7.
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:tns="uri:weblogscom" targetNamespace="uri:weblogscom" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema targetNamespace="uri:weblogscom"> <s:complexType name="pingResult"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="flerror" type="s:boolean"/> <s:element minOccurs="1" maxOccurs="1" name="message" type="s:string" /> </s:sequence> </s:complexType> </s:schema> </types> <message name="pingRequest"> <part name="weblogname" type="s:string"/> <part name="weblogurl" type="s:string"/> </message> <message name="pingResponse"> <part name="result" type="tns:pingResult"/> </message> <portType name="pingPort"> <operation name="ping"> <input message="tns:pingRequest"/> <output message="tns:pingResponse"/> </operation> </portType> <binding name="pingSoap" type="tns:pingPort"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="ping"> <soap:operation soapAction="/weblogUpdates" style="rpc"/> <input> <soap:body use="encoded" namespace="uri:weblogscom" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" namespace="uri:weblogscom" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="weblogscom"> <document> For a complete description of this service, go to the following URL: http://www.soapware.org/weblogsCom </document> <port name="pingPort" binding="tns:pingSoap"> <soap:address location="http://rpc.weblogs.com:80/"/> </port> </service> </definitions>
Frankly, that’s a lot of work for a single operation that accepts
two string parameters and returns a Boolean and a string. I had to do
all this work because WSDL makes no simplifying assumptions. I started
off specifying the request and response in the abstract. Then I had to
bind them together into an operation. I exposed the operation as a
portType
, I defined a port of that
type that accepted SOAP messages through HTTP, and then I had to expose
that port at a specific URI. For this simple case, creating the WSDL by
hand is possible (I just did it) but difficult. That’s why most WSDL is
generated by automated tools. For simple services you can start from a
generated WSDL file and tweak it slightly, but beyond that you’re at the
mercy of your tools.
The tools then become the real story. They abstract away the
service, the binding, the portType
,
the messages, the schema, and even the network itself. If you are coding
in a statically typed language, like C# or Java, you can have all this
WSDL generated for you at the push of a button. Generally all you have
to do is select which methods in which classes you want exposed as a web
service. Almost all WSDL today is generated by tools and can only be
understood by tools. After some setup, the client’s tools can call your
methods through a web service and it looks like they’re calling
native-language methods.
What’s not to like? How is this different from a compiler, which turns high-level concepts into machine code?
What ought to concern you is that you’re moving further and further away from the Web. Machine code is no substitute for a high-level language, but the Web is already a perfectly good platform for distributed programming. That’s the whole point of this book. This way of exposing programming-language methods as web services encourages an RPC style that has the overhead of HTTP, but doesn’t get any of the benefits I’ve been talking about.
Even new WSDL features like document/literal encoding (which I haven’t covered here) encourage the RPC style of web services: one where every method is a POST, and one where URIs are overloaded to support multiple operations. It’s theoretically possible to define a fully RESTful and resource-oriented web service in WSDL (something that’s even more possible with WSDL 2.0). It’s also theoretically possible to stand an egg on end on a flat surface. You can do it, but you’ll be fighting your environment the whole way.
Generated SOAP/WSDL interfaces also tend to be brittle. Different Big Web Services stacks interpret the standards differently, generate slightly different WSDL files, and can’t understand each other’s messages. The result is that clients are tightly bound to servers that use the same stack. Web services ought to be loosely coupled and resilient: they’re being exposed across a network to clients who might be using a totally different set of software tools. The web has already proven its ability to meet this goal.
Worst of all, none of the complexities of WSDL help address the travel broker scenario. Solving the travel broker problem requires solving a number of business problems, like getting “a five-minute hold on seat 24C.” Strong typing and protocol independence aren’t the solution to any of these problems. Sometimes these requirements can be justified on their own terms, but a lot of the time they go unnoticed and unchallenged, silently dragging on other requirements like simplicity and scalability.
The Resource-Oriented Alternative
WSDL serves two main purposes in real web services. It describes which interface (which RPC-style functions) the service exposes. It also describes the representation formats: the schemas for the XML documents the service accepts and sends. In resource-oriented services, these functions are often unnecessary or can be handled with much simpler standards.
From a RESTful perspective, the biggest problem with WSDL is what kind of interface it’s good at describing. WSDL encourages service designers to group many custom operations into a single “endpoint” that doesn’t respond to any uniform interface. Since all this functionality is accessible through overloaded POST on a single endpoint URI, the resulting service isn’t addressable. WADL is an alternative service description language that’s more in line with the Web. Rather than describing RPC-style function calls, it describes resources that respond to HTTP’s uniform interface.
WSDL also has no provisions for defining hypertext links, beyond the anyURI data type built into XML Schema. SOAP services aren’t well connected. How could they be, when an entire service is hidden behind a single address? Again, WADL solves this problem, describing how one resource links to another.
A lot of the time you don’t need to describe your representation
formats at all. In many Ajax applications, the client and server ends
are written by the same group of people. If all you’re doing is
serializing a data structure for transport across the wire (as happens
in the weblogs.com
ping service),
consider JSON as your representation format. You can represent fairly
complex data structures in JSON without defining a schema; you don’t
even need to use XML.
Even when you do need XML, you often find yourself not needing a formally defined schema. Sprinkled throughout this book are numerous examples of clients that use XPath expressions like “/posts/post” to extract a desired chunk out of a larger XML document. These short strings are often the only description of an XML document a client needs.
There’s nothing unRESTful or un-resource-oriented about XML Schema definitions. A schema definition is often overkill, but if it’s the right tool for the job, use it. I just think it shouldn’t be required.
UDDI
A full description of UDDI is way beyond the scope of this book. Think of it as a yellow pages for WSDL, a way for clients to look up a service that fits their needs. UDDI is even more complex than WSDL. The UDDI specification defines a four-tier hierarchical XML schema that provides metadata about web service descriptions. The data structure types you’ll find in a UDDI registry are a businessEntity, a businessService, a bindingTemplate, and a tModel.
The vision of UDDI was one of multiple registries: a fully-replicated Internet-scale registry for businesses, and a private registry behind the firewall of every company that wanted to host one. In 2006, IBM and Microsoft shut down their public UDDI registry after publicly declaring it a success. The IBM/Microsoft registry was reported to describe 50,000 businesses, but privately it was recognized that not all of that data was properly vetted, which inhibited adoption.
So sheer complexity is not the only reason why public adoption of UDDI never caught on. This is just speculation, but additional factors were probably the relatively small number of public SOAP services, successful companies’ general desire to not commoditize themselves, and WSDL’s tendency to promote a unique interface for every web service. Which is a shame, as UDDI could definitely have helped travel brokers find independently operated hotels. UDDI has seen greater success within companies, where it’s practical to impose quality controls and impose uniform interfaces.
The Resource-Oriented Alternative
There’s no magic bullet here. Any automated system that helps people find hotels has a built-in economic incentive for hotel chains to game the system. This doesn’t mean that computers can’t assist in the process, but it does mean that a human needs to make the ultimate decision.
The closest RESTful equivalents to UDDI are the search engines, like Google, Yahoo!, and MSN. These help (human) clients find the resources they’re looking for. They take advantage of the uniform interface and common data formats promoted by REST. Even this isn’t perfect: spammers try to game the search engines, and sometimes they succeed. But think of the value of search engines and you’ll see the promise of UDDI, even if its complexity turns you off.
As RESTful web services grow in popularity and become better-connected (both internally and to the Web at large), something like today’s search engines may fulfill the promise of the public UDDI registry. Instead of searching for services that expose certain APIs, we’ll search for resources that accept or serve representations with certain semantics. Again, this is speculation. Right now, the public directories of web services (I list a few in Appendix A) are oriented toward use by human beings.
Security
“Security” evokes a lot of related concepts: signatures, encryption, keys, trust, federation, and identity. HTTP’s security techniques focus pretty much exclusively on authentication and the transfer of messages. The collection of WS-* specifications related to security (and they are numerous) attempt to cover a more complete picture.
The simplest application of WS-Security is the UserName token profile. This is a SOAP “sticker” that goes on the envelope to give some context to the request: in this case, the sticker explains who’s making the request (see Example 10-8).
<Security wsse="http://schemas.xmlsoap.org/ws/2002/xx/secext"> <UsernameToken> <Username>Zoe</Username> <Password>ILoveDogs</Password> </UsernameToken> </Security>
When placed inside of the Header
section of a SOAP message, this conveys
a set of authentication credentials. It has the same qualities as HTTP
Basic authentication, an HTTP sticker which goes into the Authorization
HTTP header.
Passing passwords in clear text is not exactly best practice, especially if the channel isn’t secure. WS-Security defines a number of alternatives. To oversimplify considerably, the WS-Security specification defines a consistent set of XML element names for conveying concepts defined in other standards: passwords, SAML tokens, X.509 tokens, Kerberos tokens, and the like. There’s no reason that a similar effort couldn’t be undertaken to map similar concepts to HTTP headers. HTTP authentication is extensible, and in the early days of the development of Atom, some WS-Security concepts were ported to HTTP as WSSE (again, see Authentication and Authorization” in Chapter 8).
But Big Web Services security involves more than the WS-Security standard. Two examples:
Signatures can enable nonrepudiation. It’s possible to prove who the originator of a given message was long after it was sent, and that the message was not modified after it was sent. These concepts are important in contracts and checks.
Federation enables a third party to broker trust of identities. This would allow a travel broker to verify that a given person works for one of the travel broker’s customers: this might affect billing and discounts.
More examples are well beyond the scope of this book. Suffice it to say that security concepts are much better specified and deployed in SOAP-based protocols than in native HTTP protocols. That doesn’t mean that this gap can’t be closed, that SOAP stickers can’t be ported to HTTP stickers, or that one-off solutions are not possible without SOAP. Right now, though, SOAP has many security-related stickers that HTTP doesn’t have, and these stickers are useful when implementing applications like the travel broker.
As a caution, many of these areas are not areas where amateurs can productively dabble. Nobody should try to add new security concepts to HTTP all by themselves.
The Resource-Oriented Alternative
An application is only as secure as its weakest link. If you encrypt a credit card number for transport over the wire and then simply store it in a database, all you’ve done is ensure that attackers will target your database. Your view of security needs to encompass the entire system, not just the bits transmitted over the network.
That said, the WS-Security family of specifications are not the only tools for securing those bits. HTTPS (a.k.a Transport Layer Security [TLS], a.k.a. Secure Sockets Layer [SSL]) has proven sufficient in practice for securing credit card information as it’s sent across the network. People trust their credit cards to SSL all the time, and the vast majority of attacks don’t involve breaking SSL. The use of XML signatures and encryption is also not limited to WS-*. Section 5 of the Atom Syndication Format standard shows how to use these features in Atom documents. You’ve also seen how S3 implements request signing and access control in Chapter 3. These aspects of security are possible, and have been deployed, in RESTful resource-oriented services. But no one’s done the work to make these features available in general.
When all is said and done, your best protection may be the fact that resource-oriented architectures promote simplicity and uniformity. When you’re trying to build a secure application, neither complexity nor a large number of interfaces turn out to be advantages.
Reliable Messaging
The WS-ReliableMessaging standard tries to provide assurances to an application that a sequence of messages will be delivered AtMostOnce, AtLeastOnce, ExactlyOnce, or InOrder. It defines some new headers (that is, stickers on the envelope) that track sequence identifiers and message numbers, and some retry logic.
The Resource-Oriented Alternative
Again, these are areas where the specification and implementation for SOAP-based protocols are further advanced than those for native HTTP. In this case, there is an important difference. When used in a certain way, HTTP doesn’t need these stickers at all.
As I said earlier, almost all of the HTTP methods are idempotent. If a GET, HEAD, PUT, or DELETE operation doesn’t go through, or you don’t know whether or not it went through, the appropriate course of action is to just retry the request. With idempotent operations, there’s no difference between AtMostOnce, AtLeastOnce, and ExactlyOnce. To get InOrder you just send the messages in order, making sure that each one goes through.
The only nonidempotent method is POST, the one that SOAP uses. SOAP solves the reliable delivery problem from scratch, by defining extra stickers. In a RESTful application, if you want reliable messaging for all operations, I recommend implementing POST Once Exactly (covered back in Chapter 9) or getting rid of POST altogether. The WS-ReliableMessaging standard is motivated mainly by complex scenarios that RESTful web services don’t address at all. These might be situations where a message is routed through multiple protocols on the way to its destination, or where both source and destination are cell phones with intermittent access to the network.
Transactions
Transactions are simple to describe, but insanely difficult to implement, particularly in a distributed environment. The idea is that you have a set of operations: say, “transfer $50 from bank A to bank B,” and the entire operation must either succeed or fail. Bank A and bank B compete with each other and expose separate web services. You either want bank A to be debited and bank B to be credited, or you want nothing to happen at all. Neither debiting without crediting, or crediting without debiting are desirable outcomes.
There are two basic approaches. The WS-AtomicTransaction standard specifies a common algorithm called a two-phase commit. In general, this is only wise between parties that trust one another, but it’s the easiest to implement, it falls within the scope of existing products, and therefore it’s the one that is most widely deployed.
The second approach is defined by WS-BusinessActivity, and it more closely follows how businesses actually work. If you deposit a check from a foreign bank, your bank may put a hold on it and seek confirmation from the foreign bank. If it hears about a problem before the hold expires, it rolls back the transaction. Otherwise, it accepts the check. If it happens to hear about a problem after it’s committed the transaction, it creates a compensating transaction to undo the deposit. The focus is on undoing mistakes in an auditable way, not just preventing them from happening.
The Resource-Oriented Alternative
Again, there’s not much that corresponds to this level of specification and deployment in native HTTP applications. It’s usually not necessary at all. In Chapter 8 I implemented a transaction system by exposing the transactions as resources, but I didn’t need two-phase commit because there was only one party to the transaction. I was transferring money between accounts in a single bank. But if a number of web services supported this kind of transaction, I could stick a little bit of infrastructure on top and then orchestrate them with RESTful two-phase commit.
Two-phase commit requires a level of control over and trust in the services you’re coordinating. This works well when all the services are yours, but not so well when you need to work with a competing bank. SOA architects think two-phase commit is inappropriate for web service-based interactions in general, and I think it’s usually inappropriate for RESTful web services. When you don’t control the services you’re coordinating, I recommend implementing the ideas behind WS-BusinessActivity with asynchronous operations (again, from Chapter 8).
To go back to the example of the check from a foreign bank: your bank might create a “job” resource on the foreign bank’s web service, asking if the check is valid. After a week with no updates to that resource, your bank might provisionally accept the check. If two days later the foreign bank updates the “job” resource saying that the check is bad, your bank can create a compensating transaction, possibly triggering an overdraft and other alerts. You probably won’t need to create a complex scenario like this, but you can see how patterns I’ve already demonstrated can be used to implement these new ideas.
BPEL, ESB, and SOA
Implemented on top of the foundation I just described are some concepts that are controversial even in the world of Big Web Services. I’ll cover them briefly here.
Business Process Execution Language (BPEL) is an XML grammar that makes it possible to describe business processes that span multiple parties. The processes can be orchestrated automatically via software and web services.
The definition of an Enterprise Service Bus (ESB) varies, but tends to include discovery, load balancing, routing, bridging, transformation, and management of web service requests. This often leads to a separation of operations from development, making each simpler and easier to run.
The downside of BPEL and ESB is that they tend to increase coupling with, and reliance on, common third-party middleware. One upside is that you have a number of choices in middleware, varying from well-supported open source offerings to ones provided by established and recognized proprietary vendors.
Service-Oriented Architecture (SOA) is perhaps the least well-defined term of all, which is why I called it out in Chapter 1 as a term I wasn’t going to use. I know of no litmus test which indicates whether a given implementation is SOA or not. Sometimes a discussion of SOA starts off saying that SOA encompasses all REST/HTTP applications, but inevitably the focus turns to the Big Web Services standards I described in this chapter.
That said, one aspect of SOA is noteworthy. To date, many approaches to distributed programming focus on remote procedure calls, striving to make them as indistinguishable from local procedure calls as humanly possible. An example is a WSDL file generated from a preexisting application. The SOA idea at least returns the focus to interfaces: in particular, to interfaces that span machine boundaries. Machine boundaries tend to not happen by accident. They often correlate to trust boundaries, and they’re the places where message reliability tends to be an issue. Machine boundaries should be studied, not abstracted away
Some other aspects of SOA are independent of the technical architecture of a service. They can be implemented in resource-oriented environments, environments full of Remote Procedure Call services, or heterogeneous environments. “Governance,” for example, has to do with auditing and conformance to policies. These “policies” can be anything from government regulations to architectural principles. One possible policy might be: “Don’t make RPC-style web service calls.”
Conclusion
Both REST and web services have become buzzwords. They are chic and fashionable. These terms are artfully woven into PowerPoint presentations by people who have no real understanding of the subject. This chapter, and indeed this book, is an attempt to dispel some of the confusion.
In this chapter you’ve seen firsthand the value that SOAP brings (not so much), and the complexity that WSDL brings (way too much). You’ve also seen resource-oriented alternatives listed every step of the way. Hopefully this will help you make better choices. If you can see you’ll need some of the features described in this chapter which are only available as stickers on SOAP envelopes, getting started on the SOAP path from the beginning will provide a basis for you to build on.
The alternative is to start lightweight and apply the YAGNI (You Aren’t Gonna Need It) principle, adding only the features that you know you actually need. If it turns out you need some of the stickers that only Big Web Services can provide, you can always wrap your XML representations in SOAP envelopes, or cherry-pick the stickers you need and port them to HTTP headers. Given the proven scalability of the Web, starting simple is usually a safe choice—safe enough, I think, to be the default.
Get RESTful Web Services now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.