Cosmo and REST sitting in a tree, Part 1
Over the last several months I've designed and implemented two HTTP-based protocols for accessing Cosmo: an end user-oriented "feed service" for managing your collections of content items, subscriptions to other people's collections, and your user preferences; and Morse Code, a batch protocol used by Chandler desktop to synchronize entire collections across multiple clients.
When I started working on these protocols eight months ago, I wasn't very familiar with the concept of REST. I'd heard the term kicked around, and I'd read enough about it to know that it was a different architectural style than RPC, but I couldn't articulate the differences intelligibly and wouldn't have been able to tell you why I'd prefer a REST architecture for Cosmo. Still, enough had creeped into my subconscious that I was able to sense some of the deficiencies of RPC that we'd found and drive myself towards REST in the design of the new protocols.
For example, the calendar part of our web UI used JSON-RPC to talk the server, and that presented some clear architectural challenges. The name of the RPC operation and its parameters (including the object to call the remote method on), and sometimes the authentication credentials, were hidden inside the JSON payload in the HTTP request, so the servlet filter responsible for authenticating protocol requests and authorizing them to operate on their targets could not find the information it needed to do its job without processing the request body, a responsibility of the JSON-RPC servlet which isn't executed until after the filter has been invoked. In some circumstances, we had to pass credentials as parameters of the RPC operation and process them manually, bypassing our standard filter-based security choke point and adding explicit awareness of security concerns to additional layers.
By contrast, the resource-oriented Cosmo Management Protocol (an older, HTTP+POX version of the feed service designed for user management and other system administration tasks) uses Basic authentication, maps the HTTP method to the operation to perform on the server, and addresses the resource on which the operation is performed by URI only. This allows the security filter to step in and make decisions about authentication (extracting the credentials from the Authorization header and validating them) and authorization (determining the operation and the resource being operated on and evaluating access control rules) without ever having to process or even peek into the request body. A different software layer (the CMP servlet) is responsible for translating the POX request body into a representation of the resource and updating the resource's state. Acegi Security is usable right out of the box with just some declarative configuration; there are no compilation dependencies between the security and processing layers, whereas our JSON-RPC-based stuff had to use Acegi Security APIs explicitly in order to manually authenticate and authorize the RPC operations.
Another benefit is the individual addressing of CMP resources. This lets the server apply security rules based on a resource's URI - for example, requests to /admin/user/** are only accessible by administrative users, but any old authenticated user can access /account/view to see a web page with is account details, and any unauthenticated user can pull up /account/signup to get a web page that lets them sign up for an account. With the JSON-RPC setup, all requests were POSTed to a single URI, so there was no opportunity to use it to discriminate between security rules. Had we used JSON-RPC for any part of our web Ui other than the personal calendar interface, the RPC implementation layer would have had to manually select and evaluate security rules based on the RPC method call.
I didn't realize any of this at the time that I designed CMP a couple of years ago; like a naive teenager, I just did what felt natural. We wanted a network protocol that administrators could easily script, and I figured using HTTP's built-in verb set would make it easy to script tasks with Perl+LWP or curl. When Bobby proposed JSON-RPC for the Ajax calendar UI, I didn't see anything wrong with it. Neither of us knew any better. We figured hey, there's a Javascript library for it, so let's get something working and move on from there. The problems only became obvious over time, but of course by then we were fairly well invested in the existing JSON-RPC implementation and had to wait for a suitable point in our development schedule before we could consider a wholesale replacement. And that's where the feed service I mentioned above comes in.
I'll talk more about the feed service in subsequent posts and discuss topics that will have you on the edge of your seat like: the difference between the feed as a general purpose data representation and the feed meant to be consumed by a feed reader, the joys of synchronization over HTTP, the folly of inventing your own XML dialect when an existing one will do, how to build an Atom-based web service with Abdera, how I beergoggled GData but saved myself from having to gnaw my arm off the next morning, and how reading Leonard Richardson and Sam Ruby's RESTful Web Services could have saved me a big chunk of the 8 months I put into Morse Code and the feed service.