logo  AtomServer, Protocol Reference

Contents

Protocol details

This section describes the AtomServer document format and query syntax.

Document format

AtomServer and Atom share the same basic data model: a container that holds both some global data and any number of entries. For each protocol, the format is defined by a base schema, but it can be extended using foreign namespaces.

Note: AtomServer feeds are in Atom format and thus, use the Atom namespace as the default namespace by specifying an xmlns attribute on the feed element; see the examples section for examples of how to do that. Thus, the examples in this document don't explicitly specify atom: for elements in an Atom-format feed.

The following tables show the Atom  representations of the elements of the schema. All data not mentioned in these tables is treated as plain XML and shows up the same in both representations. Unless indicated otherwise, the XML elements in a given column are in the namespace corresponding to that column. This summary uses standard XPath notation: in particular, slashes show the element hierarchy, and an @ sign indicates an attribute of an element.

In each of the following tables, the highlighted items are required.

The following table shows the elements of a AtomServer feed:

Feed Schema Item Atom Representation
Feed Title /feed/title
Feed ID /feed/id
Feed HTML Link /feed/link[@rel="alternate"]\
[@type="text/html"]/@href
Feed Language /feed/@xml:lang
Feed Author

/feed/author/name

(Required in certain cases; see Atom specification.)

Feed Last Update Date /feed/updated
(RFC 4287 format) See the About Date Constructs section for further details.

The following table shows the elements of a AtomServer feed. Note that AtomServer exposes some of the OpenSearch 1.1 Response elements in its search-results feeds.

Search Result Feed Schema Item Atom Representation
Number of Search Results /feed/openSearch:totalResults
Search Result Start Index /feed/openSearch:startIndex
Number of Search Results Per Page /feed/openSearch:itemsPerPage

The following table shows the elements of a AtomServer Entry:

Entry Schema Item Atom Representation
Entry ID /feed/entry/id
Entry Title /feed/entry/title
Entry Link /feed/entry/link
Entry Content

/feed/entry/content

(If no content element, then entry must contain at least one <link rel="alternate"> element.)

Entry Publication Date /feed/entry/published
(RFC 4287)  See the About Date Constructs section for further details.
Entry Update Date /feed/entry/updated
(RFC 4287)  See the About Date Constructs section for further details.

Queries for Time-sensitive Feeds

This section describes how to use the query system to request Feeds that are time-sensitive. The query model is intentionally very simple. The basic tenet is that queries are expressed as HTTP URIs, rather than as HTTP headers or as part of the payload. One benefit of this approach is that you can link to a query.

Query requests

A client queries the AtomServer service by issuing an HTTP GET request. The query URI consists of the resource's URI (called FeedURI in Atom) followed by query parameters. Most query parameters are represented as traditional ?name=value[&...] URL parameters. Category parameters are handled differently; see below.

For example, if the FeedURI is http://foo.com/myserver/v1/widgets/, then you might send a query with the following URI:

http://foo.com/myserver/v1/widgets/acme?updated-min=2005-04-19T15:30:00

AtomServer services support HTTP Conditional GET. They set the Last-Modified response header based upon the value of the <atom:updated> element in the returned feed or entry. A client can send this value back as the value of the If-Modified-Since request header to avoid retrieving the content again if it hasn't changed. If the content hasn't changed since the If-Modified-Since time, then the AtomServer service returns a 304(Not Modified) HTTP response.

AtomServer supports category queries. Passing a standard parameter not understood by a given service results in a 403 Forbidden response. Passing an unsupported nonstandard parameter results in a 400 Bad Request response. For information on other status codes, see the HTTP status codes section of this document.

The standard query parameters are summarized in the following table. All parameter values need to be URL encoded.

Parameter Meaning Notes
/-/category Category queries
  • List the Category as if it were part of the resource's URI, in the form /-/categoryname. Note, this is an exception to the usual name=value form for Query parameters
  • List all categories before any other query parameters.
  • NOTE: see the Categories document for further details on Category Queries
updated-min, updated-max Bounds on the entry update date
  • Use the RFC 4287 timestamp format. For example: 2005-08-09T10:57:00-08:00.
  • The lower bound is inclusive, whereas the upper bound is exclusive.
  • See the About Date Constructs section for further details.
  • NOTE: updated-max has NOT yet been implemented
start-index The index of the last result retrieved
  • Note that this isn't a general cursoring mechanism. If you first send a query with ?start-index=1&max-results=10 and then send another query with ?start-index=11&max-results=10, the service cannot guarantee that the results are equivalent to ?start-index=1&max-results=20, because insertions and deletions could have taken place in between the two queries.
  • Note that start-index should NOT be interpreted in any way. It is used to order entries, but you should never assume its value. The only reliable way to determine start-index is use the next link within the Feed, or to use the value returned in the openSearch:startIndex element.
  • start-index is "exclusive". In other words, it is used as "> start-index" in the resulting query.
max-results Maximum number of results to be retrieved
  •  max-results must be <= 100 for link feeds, and <= 20 for full feeds. Any value greater of max-results than this will be reset to these maximums. A link feed is is a feed that contains links to Entrys. And a full feed contains the entire Entry, including its content. (See entry-type below)
locale
The locale Id of the requested info
  • Locales MUST conform to the Java standard. Locales are of the form language_country. For example; en_US.
    • The language argument must be a valid ISO Language Code.  These codes are the lower-case, two-letter codes as defined by ISO-639. You can find a full list of these codes here.
    • The country argument must be a valid ISO Country Code. These codes are the upper-case, two-letter codes as defined by ISO-3166. You can find a full list of these codes here.
    • The variant argument is a vendor or browser-specific code sometimes used in locales, and is NOT ALLOWED in atomserver.
  • You may use the locale parameter with both the "collection form" (i.e. ../widgets/acme?locale=en_GB) or with the "entry form" (i.e. ../widgets/acme/1234?locale=en_GB), but when you use it with the entry form you must be careful to not use the "full form" (i.e. ../widgets/acme/1234.en_GB.xml) because the full form takes precedence.
entry-type
The type of Entry to return.
  • Indicates the type of Entry to return. The choices are full or link.
  • full causes a "full Entry" to be returned (i.e. the <content> contains the entirety of the XML data requested).
  • link causes a "link Entry" to be returned (i.e. no <content> element is returned. Instead links (self and edit) are provided to the data requested.
  • The default for Feeds is link, and the default for Entry is full.
entryID.localeID.xml ID of a specific entry to be retrieved
  • If you specify an entry Id, you can't specify any other parameters.
  • Unlike most of the other query parameters, entry Id is specified as part of the URI, not as a name=value pair.
  • Example: http://foo.com/myserver/v1/widgets/acme/1234.en.xml.
  • Locale is optional, depending on how the Workspace or Collection is configured
  • Locales MUST conform to the Java standard. Locales are of the form language_country. For example; en_US.
    • The language argument must be a valid ISO Language Code.  These codes are the lower-case, two-letter codes as defined by ISO-639. You can find a full list of these codes here.
    • The country argument must be a valid ISO Country Code. These codes are the upper-case, two-letter codes as defined by ISO-3166. You can find a full list of these codes here.
  • Note that it is possible to specify only the entry Id, although you use must also specify the locale parameter (see above).

Query responses

Queries return an Atom Feed or an Atom Entry, depending on the request parameters.

Query results contain the following OpenSearch elements directly under the <feed> element:

openSearch:totalResults
The total number of search results for the query. This element is optional, and is not provided by default.
openSearch:startIndex
The index of the first result. See the start-index in the table above for important information about this element.
openSearch:itemsPerPage
The maximum number of items that appear on one page. This allows clients to generate direct links to any set of subsequent pages. However, for a possible pitfall in using this number, see the note regarding start-index in the table in the Query requests section.

Query results also contain the following AtomServer extension elements directly under the <feed> element:

atomserver:endIndex
The index of the final result. See the start-index description in the table above for important information about this element. Note; the endIndex returned by a Feed can be used as the start-index for a subsequent Feed page request.

The Atom response Feed and Entries may also include any of the following Atom and AtomServer elements (as well as others listed in the Atom specification):

<link rel="self" type="..." href="..."/>
Contains the URI of this resource. The value of the type attribute depends on the requested format. If no data changes in the interim, sending another GET to the self URI returns the same response.
<link rel="next" type="application/atom+xml" href="..."/>
Specifies the URI of the next chunk of this query result set, if it is chunked. The client must use the next link when accessing the next page of results.

Query results also contain the following AtomServer extension elements directly under the <entry> element:

atomserver:entryId
The Id of the Atom Entry. This is element is provided as a convenience to the User. It is particularly important for POST requests, when the Id was created within AtomServer. It saves the User the trouble of parsing the Id out of the <id> element, because inevitably the User will need it for further processing.

Here's a sample response body, in response to a Feed query:

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:as="https://atomserver.org/namespaces/1.0/"
xmlns:os="http://a9.com/-/spec/opensearchrss/1.1/">
<os:totalResults>65801</os:totalResults>
<os:startIndex>0</os:startIndex>
<os:itemsPerPage>2</os:itemsPerPage>
<as:endIndex>153</as:endIndex>
<link href="/myserver/v1/widgets/acme?start-index=153&amp;max-results=2" rel="next" />
<link href="/myserver/v1/widgets/acme?start-index=0&amp;max-results=2" rel="self" />
<author><name>AtomServer APP Service</name></author>
<title type="text">acme entries</title>
<updated>2007-10-05T19:17:42.750Z</updated>
<id>tag:atomserver.org,2007:v1:acme</id>
<entry>
<id>/myserver/v1/widgets/acme/205390.en.xml</id>
<as:entryId>205390</as:entryId>
<updated>2007-10-05T18:48:23.437Z</updated>
<published>2007-10-05T18:48:23.437Z</published>
<title type="text"> Entry: acme 205390.en</title><author>
<name>AtomServer Atom Service</name></author>
<link href="/myserver/v1/widgets/acme/205390.en.xml" rel="self" />
<link href="/myserver/v1/widgets/acme/205390.en.xml/2" rel="edit" />
<link href="/myserver/v1/widgets/acme/205390.en.xml" rel="alternate" />
</entry>
<entry>
<id>/myserver/v1/widgets/acme/205395.en.xml</id>
<as:entryId>205395</as:entryId>
<updated>2007-10-05T15:48:56.437Z</updated>
<published>2007-10-05T12:50:76.437Z</published>
<title type="text"> Entry: acme 205395.en</title><author>
<name>AtomServer Atom Service</name></author>
<link href="/myserver/v1/widgets/acme/205395.en.xml" rel="self" />
<link href="/myserver/v1/widgets/acme/205395.en.xml/1" rel="edit" />
<link href="/myserver/v1/widgets/acme/205395.en.xml" rel="alternate" />
</entry>
</feed>

If the requested feed is in the Atom format, if no query parameters are specified, and if the result doesn't contain all the entries, the following element is inserted into the top-level feed: <link rel="next" type="application/atom+xml" href="..."/>. It points to a feed containing the next set of entries. Subsequent sets contain a corresponding <link rel="previous" type="application/atom+xml" href="..."/> element. By following all the next links, a client can retrieve all entries from a feed. Note that following "next links" is the only reliable way to retrieve pages.

Dealing with Time-sensitive Feeds

A time-sensitive Feed is one that involves requests using the Last Modified Date, which is the date from which you expect to receive Entries that have have been modified.  Using AtomServer, it is possible to specify the Last Modified Date two different ways; using the If-Modified-Since HTTP Request Header, or using the updated-min Query Request Parameter (e.g. widgets/acme?updated-min=2005-04-19T15:30:00 )

The Last Modified Date must be provided using the RFC4287 standard, which is detailed below.  And the Last Modified Date is inclusive. In other words, the time-sensitive Feed will contain all Entries from the Last Modified Date onwards, including those at the Last Modified Date itself.

It is important to understand that a time-sensitive Feed contains any Entries that have been modified since the provided date. And since AtomServer is a store, Entries may be continually modified. This means that it is entirely possible that you will see Entries you've seen previously in a Feed. For example, Let's imagine that you've requested a Paged Feed, and you've seen Entries A, B, C ,D, and E, and let's say that B has been deleted (remember, deleted Entries are not really deleted, but are simply marked as deleted) and that D has been modified. Then you will see both B and D again in a later page.

The best way to access a time-sensitive Feed is to use the start-index URL Query (e.g. GET /v1/widgets/acme?start-index=153). Using this method ensures that you will never receive any inappropriate duplicate Entries. The reason for this is that a "last modified date" operation is, by definition, an inclusive operation (i.e. ">="). And that many Entries may have the same Date. To work around this, AtomServer indexes every Entry monotonically. And a "get next index" operation is exclusive (i.e. ">"), so you are therefore assured of getting the appropriate next Entry. The start-index for a Feed is equal to the endIndex returned by the last page you received.

The RFC4287 Date Specification

Any Dates provided must match the RFC4287 specification, which is described below. Most important, dates are assumed to be in GMT, unless you provide a timezone offset!

  3.3.  Date Constructs

  A Date construct is an element whose content MUST conform to the
  "date-time" production in [RFC3339].  In addition, an uppercase "T"
  character MUST be used to separate date and time, and an uppercase
  "Z" character MUST be present in the absence of a numeric time zone
  offset.

  atomDateConstruct =
     atomCommonAttributes,
     xsd:dateTime

  Such date values happen to be compatible with the following
  specifications: [ISO.8601.1988], [W3C.NOTE-datetime-19980827], and
  [W3C.REC-xmlschema-2-20041028].

  Example Date constructs:

  <updated>2003-12-13T18:30:02Z</updated>
  <updated>2003-12-13T18:30:02.25Z</updated>
  <updated>2003-12-13T18:30:02+01:00</updated>
  <updated>2003-12-13T18:30:02.25+01:00</updated>

  Date values SHOULD be as accurate as possible.  For example, it would
  be generally inappropriate for a publishing system to apply the same
  timestamp to several entries that were published during the course of
  a single day.

Optimistic concurrency (versioning)

It is important to ensure that multiple clients don't inadvertently overwrite one another's changes. This is most easily accomplished by ensuring that the current version of an Entry that a client is modifying is the same as the version that the client is basing its modifications on. If a second client makes an update before the first client does, then the first client's update is denied, because the first client is no longer basing its modifications on the latest version.

In AtomServer, we achieve these semantics by appending a version ID to each entry's edit URI. Note that only the edit URI is affected, not the entry ID. In this scheme, each update changes the entry's edit URI, thus guaranteeing that subsequent updates based on the original version fail. Deletes, of course, are identical to updates with respect to this feature; if you send a delete with an old version number, the delete fails.

If the server detects a version conflict on PUT or DELETE, the server responds with 409 Conflict. The body of the response contains the edit URI of the entry. The client is advised to resolve the conflict and resubmit the request, using the edit URI from the 409 response. An example error response follows;

<?xml version='1.0' encoding='UTF-8'?>
<a:error xmlns:a="http://incubator.apache.org/abdera">
   <a:code>409</a:code>
   <a:message>Optimistic Concurrency Error:: /myserver/v1/widgets/acme/12345.en.xml/2</a:message>
   <link xmlns="http://www.w3.org/2005/Atom" href="/myserver/v1/widgets/acme/12345.en.xml/3" rel="edit" />
</a:error>

A couple of things to note:
    * you are not required to send the version ID on a GET (in fact, it is discouraged), but if you supply one, it must be correct, and you will get a 409 Conflict if it is wrong
    * if you try to DELETE a resource that does not exist, you will get a 404 Not Found response.

Overriding Optimistic Concurrency

By its very nature, Optimistic Concurrency is easily ignored by clients - that is to say, whenever AtomServer refuses to write a resource because of a mismatched version ID, it will respond telling the client what the correct version ID is. If the client wishes, it can always use that link to immediately overwrite the resource. In the case of resources that have a "single writer", where you will always know that the data you want to write should be written, you can avoid having to make that first round trip to determine the version ID by specifying a version ID of "*". For example, if you do a PUT to http://foo.com/myserver/v1/widgets/acme/1234.en.xml/*, your write will succeed, regardless of the current version ID. The response will still contain the correct entry link and version ID.

Motivation and design notes

This approach to optimistic concurrency allows us to implement the semantics we want without requiring new markup for version IDs, which makes AtomServer's responses compatible with non-AtomServer Atom endpoints.

Instead of specifying version IDs, we could have chosen to look at the update timestamp on each entry (/atom:entry/atom:updated). However, there are two problems with using the update timestamp:

    * It only works for updates and not deletions.
    * It forces applications to use date/time values as version IDs, which are problematic.

Locale sensitivity

At one time AtomServer was "locale sensitive", but there is no longer any "locale sensitivity". What this means is that you must be specific when requesting locales, and less generic locales are no longer returned when their specific counterparts are requested. For example, if you request 123.en_GB.xml and there is none, but there is a 123.en.xml, you will not receive 123.en.xml - you will receive a 404 NOT FOUND. Similar for Feed requests, when you request /widgets?locale=en_GB, you will receive only en_GB Entries -- and if there are none; 404. It was decided that this behavior was more consistent with a "Data Service". And that locale sensitivity was confusing and complicating things in the client Feed Readers.

HTTP status codes

The following table describes what various HTTP status codes mean in the context of AtomServer.

Code Explanation
200 OK No error.
201 CREATED Creation of a resource was successful.
304 NOT MODIFIED The resource hasn't changed since the time specified in the request's If-Modified-Since header.
400 BAD REQUEST Invalid request URI or header, or unsupported nonstandard parameter.
401 UNAUTHORIZED Authorization required.
403 FORBIDDEN Unsupported standard parameter, or authentication or authorization failed.
404 NOT FOUND Resource (such as a feed or entry) not found.
409 CONFLICT Specified version number doesn't match resource's latest version number.
422 BAD CONTENT
The data within this entry's <content> is not valid. For example, this may indicate not "well-formed" XML
500 INTERNAL SERVER ERROR Internal error. This is the default code that is used for all unrecognized errors.

Additional resources

You may find the following third-party documents useful:

Back to top