XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Implementing the Atom Publishing Protocol

Implementing the Atom Publishing Protocol

July 19, 2006

The Atom Publishing Protocol (APP) is nearing completion, many of the issues that I pointed out in a previous article have settled down, and there is work being done on implementations and interoperability. Although the interoperability work will go on for years to come, we can put together an implementation and discuss the requirements the APP puts on you, the gotchas, and the ways we can optimize the service. If you've been following along with Restful Web columns at home, you won't be surprised that the implementation is in Python. In future articles we'll start building more complex services on top of this APP implementation.

Before we dive into the code, let's back up and take a high-level look at what we need to implement. For reference, we'll be implementing draft -08 of the Atom Publishing Protocol. At the conclusion of "How to Create a REST Protocol" the four primary questions about a REST protocol are answered and a table describes the protocol. Table 1 is just such a table for the Atom Publishing Protocol.

Table 1.

Resource

HTTP Method

Representation

Description

Introspection

GET

Introspection Document

Enumerates a set of collections and lists their URIs and other information about the collections.

Collection

GET

Atom Feed

A list of member of the collection. Note that this may be a subset of all entries in the collection.

Collection

POST

Atom Entry

Create a new entry in the collection.

Member

GET

Atom Entry

Get the Atom Entry.

Member

PUT

Atom Entry

Update the Atom Entry.

Member

DELETE

N/A

Delete the Atom Entry from the collection.

All of these operations are pretty obvious, except using GET on a collection. In that case, the response to a GET may not return all of the entries in a collection. Actually, if it's a large collection I really hope it doesn't return all the entries in the collection. So the initial GET returns what may be a subset of the entries in the collection, ordered in reverse chronological order of their atom:updated date. Note that this means the most recently updated entries are returned first in the feed. If the feed returned doesn't contain all the entries in the collection, then the feed will contain an atom:link element of type "next" that points to another feed with the next set of entries in the collection -- it will also be ordered in reverse atom:updated chronological order. Reverse atom:updated chronological order is quite a mouthful, and it doesn't even abbreviate very nicely; "RAUCO" sounds more like a Soprano's character than a technical term.

Table 1 is good, but if we are going to generate a concrete implementation, then we need to add more detailed information. We'll add another column for the URIs of each of the resources and drop the description column to save space (see Table 2).

Table 2.

URI

Resource

HTTP Method

Representation

/collection/introspection/

Introspection

GET

Introspection Document

/collection/

Collection

GET

Atom Feed

/collection/

Collection

POST

Atom Entry

/collection/member/{id}

Member

GET

Atom Entry

/collection/member/{id}

Member

PUT

Atom Entry

/collection/member/{id}

Member

DELETE

N/A

Of course, those aren't really URIs in the URI column -- the last three rows have URI Templates instead of URIs. That is, you substitute the string {id} with some value, in this case some unique identifier for each entry in the collection, in order to obtain a full URI. The idea of URI Templates isn't new; I provided code for how to handle URI Template variable expansion in "Constructing or Traversing URIs?". We now need to take three short side trips to gather the pieces we need to put together our implementation of the APP.

A Place for My Stuff

The first thing we will need is a place to store our Atom Entries. Let's look back at Table 2 and see about our requirements. Each entry needs to be able to retrieved, updated, and deleted based on a single id. Here is a sketch of the interface for the Python class:

class Store:

    def get(self, id):
        pass

    def delete(self, id):
        pass

    def put(self, id, entry):
        pass

We'll assume that the entry taken in put() and returned by get() will be in the form of a string. In addition, we need the ability to add new entries to the collection using just an entry document, and that creation process needs to report the id that was assigned the new entry.

class Store:

    def get(self, id):
        pass

    def delete(self, id):
        pass

    def put(self, id, entry):
        pass

    def post(self, entry):
        pass

And, finally, we need to enumerate the members of a collection, which means we have to generate a linked chain of atom feeds that enumerate the entries in the collection in reverse atom:updated order (aka el-RAUCO). If we assume that we will generate the feeds with a fixed number of entries per feed, then we need a way to query with a size and offset from the beginning of the list. The index method will just return a subset of the entries' ids that are of size length and the first entry is offset back in the list.

class Store:

    def get(self, id):
        pass

    def post(self, entry):
        pass

    def delete(self, id):
        pass

    def put(self, id, entry);
        pass

    def index(self, size, offset):
        pass

Another requirement, which we have by virtue of building a web service, is that the underlying data store needs to be able to safely operate when being accessed simultaneously by multiple processes or threads. That requirement was met by building the underlying store on top of SQLite, which automatically handles accesses from multiple processes or threads. The Store also parses each incoming entry to ensure that it is at least well-formed and also updates fields that need to be updated. For example, on creation an entry will be assigned a unique id and that id will overwrite the value in the atom:id. There are also hooks for custom behaviors on updates. I will save the rest of the technical details of the Store class for another day. What I really want to concentrate on now is how implementing a RESTful protocol with the right tools is easy and the advantages you can get from using HTTP correctly.

Pages: 1, 2, 3

Next Pagearrow







close