<?xml version="1.0" encoding="UTF-8"?>
<book xmlns:ci="http://apache.org/cocoon/include/1.0">
  <bookinfo>
    <graphic fileref="logo.jpg" />

    <productname>Open Source Native XML Database</productname>

    <title>Atom Publishing Protocol Support</title>

    <author>
      <surname>Milowski</surname>

      <firstname>Alex</firstname>

      <email>alexmilowski@users.sourceforge.net</email>
    </author>
  </bookinfo>

  <ci:include src="sidebar.xml" />

  <chapter>
    <title>A Short Introduction to the Atom Publishing Protocol</title>

    <section>
      <title>Overview</title>

      <para>The <ulink
      url="http://www.ietf.org/internet-drafts/draft-ietf-atompub-protocol-09.txt">Atom
      Publishing Protocol</ulink> is a new protocol being developed at the
      <ulink url="http://www.ietf.org/">IETF</ulink> as part of their <ulink
      url="http://www.ietf.org/html.charters/atompub-charter.html">Atom
      Publishing Format and Protocol</ulink> working group that lets you
      manipulate Atom feeds over a REST-style HTTP interface. The RFC is still
      in draft status but is nearing completion. </para>
    </section>

    <section>
      <title>Creating and Manipulating Atom Entries</title>

      <para>To create an entry in the feed, you just POST an [atom:]entry
      element to the collection url. This creates a new entry in the feed and
      possibly returns a new representation of the entry. For example, to
      create a simple annotation entry (possibly a blog post), you'd formulate
      an [atom:]entry element using the [atom:]content element and post that
      to the server.</para>

      <example>
        <title>Create Entry</title>

        <screen>POST /myblog/entries HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/atom+xml
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;entry xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;Atom-Powered Robots Run Amok&lt;/title&gt;
   &lt;id&gt;urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a&lt;/id&gt;
   &lt;updated&gt;2003-12-13T18:30:02Z&lt;/updated&gt;
   &lt;author&gt;&lt;name&gt;John Doe&lt;/name&gt;&lt;/author&gt;
   &lt;content&gt;Some text.&lt;/content&gt;
&lt;/entry&gt;</screen>
      </example>

      <para>The server then responds with the created entry, a location header
      to the created entry, or both.</para>

      <example>
        <title>Created Response</title>

        <screen>HTTP/1.1 201 Created
Date: Fri, 7 Oct 2005 17:17:11 GMT
Content- Length: nnn
Content- Type: application/atom+xml; charset="utf-8"
Content- Location: http://example.org/edit/first-post.atom
Location: http://example.org/edit/first-post.atom

&lt;?xml version="1.0"?&gt;
&lt;entry xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;Atom-Powered Robots Run Amok&lt;/title&gt;
   &lt;id&gt;urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a&lt;/id&gt;
   &lt;updated&gt;2003-12-13T18:30:02Z&lt;/updated&gt;
   &lt;author&gt;&lt;name&gt;John Doe&lt;/name&gt;&lt;/author&gt;
   &lt;content&gt;Some text.&lt;/content&gt;
   &lt;link rel="edit"
         href="http://example.org/edit/first-post.atom"/&gt;
&lt;/entry&gt;
</screen>
      </example>

      <para>In the above example response, notice the 'edit' relation link.
      This link will let you change this entry in the future. A PUT request on
      that link URL will update that entry in the feed. Similarly, a DELETE
      request on that link URL will delete the entry.</para>
    </section>

    <section>
      <title>Uploading Resources</title>

      <para>Collections can also contain non-atom entry resources that are
      also represented by an [atom:]entry in the feed. These are created by a
      POST to the same collection URL. The response is an [atom:]entry
      element.</para>

      <example>
        <title>Image Upload</title>

        <screen>POST /myblog/fotes HTTP/1.1
Host: example.org
Content- Type: image/png
Content- Length: nnnn
Title: An Atom-Powered Robot

...binary data...</screen>
      </example>

      <para>In the above request, a title for the entry can be requested when
      the resource is created. The server then responds with the created
      entry, a location header to the created entry, or both.</para>

      <example>
        <title>Upload Response</title>

        <screen>HTTP/1.1 201 Created
Date: Fri, 7 Oct 2005 17:17:11 GMT
Content- Length: nnn
Content- Type: application/atom+xml; charset="utf-8"
Content- Location: http://example.org/edit/first-post.atom
Location: http://example.org/edit/first-post.atom

&lt;?xml version="1.0"?&gt;
&lt;entry xmlns="http://www.w3.org/2005/Atom"&gt;
  &lt;title&gt;A picture of the beach&lt;/title&gt;
  &lt;id&gt;urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a&lt;/id&gt;
  &lt;updated&gt;2003-12-13T18:30:02Z&lt;/updated&gt;
  &lt;author&gt;&lt;name&gt;John Doe&lt;/name&gt;&lt;/author&gt;
  &lt;summary type="text" /&gt;
  &lt;content type="image/png"
           src="http://example.org/media/img123.png"/&gt;
  &lt;link rel="edit"
         href="http://example.org/edit/first-post.atom" /&gt;
  &lt;link rel="edit-media"
        href="http://example.org/edit/img123.png" /&gt;
&lt;/entry&gt;</screen>
      </example>

      <para>In the above example response, notice the 'edit-media' relation
      link in addition to the 'edit' relation link. The 'edit-media' link
      relation lets you update the image while the 'edit' link relation lets
      you change the entry as in the previous section. Also, a delete on
      either editing link relation will delete the resource and its
      corresponding entry.</para>
    </section>

    <section>
      <title>Introspection</title>

      <para>To get started with an Atom Publishing Protocol enabled service
      you need to know the collection URL. A GET request on that collection
      URL will return the feed and that same URL is used to create entries.
      One way to bootstrap a client and get that collection URL is through
      service introspection.</para>

      <para>Given a service URL, a GET request will return an XML document
      that enumerates the workspaces and collections available to use. This is
      a simple document of the following structure:</para>

      <example>
        <title>An Introspection Document</title>

        <screen>&lt;?xml version="1.0" encoding='utf-8'?&gt;
&lt;service xmlns="http://purl.org/atom/app#"&gt;
  &lt;workspace title="Main Site" &gt;
    &lt;collection
      title="My Blog Entries"
      href="http://example.org/reilly/main" /&gt;
    &lt;collection
      title="Pictures"
      href="http://example.org/reilly/pic" &gt;
      &lt;accept&gt;image/*&lt;/accept&gt;
    &lt;/collection&gt;
  &lt;/workspace&gt;
  &lt;workspace title="Side Bar Blog"&gt;
    &lt;collection title="Remaindered Links"
      href="http://example.org/reilly/list" /&gt;
  &lt;/workspace&gt;
&lt;/service&gt;

</screen>
      </example>

      <para>Each 'collection' element represents a feed that can be modified.
      The colleciton URL is specified in the 'href' attribute on that
      element.</para>
    </section>
  </chapter>

  <chapter>
    <title>Atom Services</title>

    <section>
      <title>Modules</title>

      <para>The Atom services are provided by modules loaded into the Atom
      service. This service is normally provided at the server's '/atom' path
      (e.g. http://localhost:8088/atom/). Every module has a name and to
      access the module the name is appended to the atom service path. For
      example, for the 'content' module, the URL
      http://localhost:8088/atom/content would be used.</para>

      <para>Following the module name is a collection or resource path. That
      appened path maps to a collection or resource in the database with the
      /atom/{name} prefix removed. For example,
      'http://localhost:8088/atom/content/myfeed' is the '/myfeed' collection
      while 'http://localhost:8088/atom/content' is the root collection of the
      database.</para>

      <para>The Atom Publishing Protocol makes a distinction between a
      collection and the feed that represents it. In eXist, a similar
      distinction exists in that a collection is a set of resources and not a
      feed. The exception is that the Atom implementation makes a one XML
      document special and uses that to represent the collection as a
      feed.</para>

      <para>Currently, if a collection is an also an Atom feed, a document
      named '.feed.atom' must exist in the collection. If so, the Atom
      interface servlet will treat the collection as a Atom feed that can be
      manipulated. If that document does not exist, a module may respond with
      a failure status code.</para>
    </section>

    <section>
      <title>The Content Module</title>

      <para>Getting the feed is a simple GET request with the "/atom/content"
      prefix on the collection path. For example, if you have a collection at
      '/my/blog', then the path '/atom/content/my/blog' will return the
      feed.</para>
    </section>

    <section>
      <title>The Edit Module</title>

      <para>The 'edit' module allows you to create new feeds and manipulate
      existing feeds. This module implements the full Atom Publishing protocol
      except for the service introspection part.</para>

      <para>In addition to implementing the Atom Publishing Protocol, you can
      create a manipulate feeds on collections. To create a new collection or
      turn an existing collection into a feed, POST an [atom:]feed element to
      the collection path with a /atom/edit/ prefix. This will create a new
      collection feed using the feed title, etc. specified in the post.</para>

      <para>To change the metadata associated with the collection's feed (e.g.
      the feed title), make a PUT request to the collection URL with the
      updated [atom:]feed element without any [atom:]entry (these will be
      ignored). That will change the feed's metadata.</para>
    </section>

    <section>
      <title>The Introspect Module</title>

      <para>Service introspection is provided by the 'introspect' module. A
      GET request on any collection path prefixed with /atom/introspect will
      return a service introspection document.</para>
    </section>

    <section>
      <title>The Query Module</title>

      <para>The 'query' module allows you to query your collection's feed by a
      POST of an XQuery. The POST is sent to the collection path prefixed with
      /atom/query/ and is run against the collection's feed document. This
      module is how both the Introspect and Topic modules are implemented. For
      example, if you want to generate a feed of all the feeds in all
      subcollections, the following query could be send:</para>

      <example>
        <title>A Feed Query</title>

        <screen>&lt;feed xmlns="http://www.w3.org/2005/Atom" xmlns:atom="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;All Feeds Example&lt;/title&gt;
   &lt;id&gt;id:all&lt;/id&gt;
   {
     let $current := substring-before(base-uri(/atom:feed),'/.feed.atom'),
         $current-path := substring-after($current,'/db')
        for $i in (collection($current)/atom:feed)
           let $path :=  substring-after(substring-before(base-uri($i),'/.feed.atom'),'/db')
              return ( &lt;entry&gt;
                        {
                           $i/atom:id,
                           $i/atom:title,
                           &lt;summary&gt; {
                               $i/atom:subtitle/@type,
                               $i/atom:subtitle/node()
                           }&lt;/summary&gt;,
                           &lt;link rel="alternate" type="application/atom+xml" href="/atom/edit{$path}"/&gt;
                       }
                       &lt;/entry&gt;
                     )
   }
&lt;/feed&gt;
</screen>
      </example>
    </section>

    <section>
      <title>The Topic Module</title>

      <para>The 'topic' module is an example module that generates a list of
      subtopics give a certain feed. A GET request will return a feed with the
      collection path's feed, an entry for each ancestor feed, and an entry
      for each sub-collection but not any sub-collections of sub-collections.
      This is useful for generation feed navigation.</para>
    </section>
  </chapter>

  <chapter>
    <title>An Example</title>

    <section>
      <title>Example Scenario</title>

      <para>A blog feed can easily be implemented using the Atom services.
      We'll create a blog where all the entries are in the feed as well as any
      resources (e.g. images) are also in the same feed.</para>

      <para>In this system, we want a /{username}/blog to be the user's blog.
      That way we can search against all the user blogs.</para>
    </section>

    <section>
      <title>Setting Up the Blog Feeds</title>

      <para>We'll want a top-level blog so we can post queries. This feed can
      be empty and is created as:</para>

      <example>
        <title>Create Users Feed</title>

        <screen>POST /atom/edit/users HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/atom+xml
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;feed xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;All Users&lt;/title&gt;
&lt;/feed&gt;</screen>
      </example>

      <para>When a user is created, we'll have to post a new [atom:]feed
      element to the user directory and blog:</para>

      <example>
        <title>Create User</title>

        <screen>POST /atom/edit/users/johndoe HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/atom+xml
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;feed xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;User John Doe&lt;/title&gt;
   &lt;author&gt;&lt;name&gt;John Doe&lt;/name&gt;&lt;/author&gt;
&lt;/feed&gt;</screen>
      </example>

      <example>
        <title>Create User Blog</title>

        <screen>POST /atom/edit/users/johndoe/blog HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/atom+xml
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;feed xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;John Doe's Blog&lt;/title&gt;
   &lt;author&gt;&lt;name&gt;John Doe&lt;/name&gt;&lt;/author&gt;
&lt;/feed&gt;</screen>
      </example>
    </section>

    <section>
      <title>Creating Entries</title>

      <para>Now a user can post a new entry to their blog:</para>

      <example>
        <title>Create a New Entry</title>

        <screen>POST /atom/edit/users/johndoe/blog HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/atom+xml
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;entry xmlns="http://www.w3.org/2005/Atom"&gt;
   &lt;title&gt;My First Entry&lt;/title&gt;
   &lt;content type='xhtml'&gt;
      &lt;div xmlns='http://www.w3.org/1999/xhtml'&gt;
         &lt;p&gt;Isn't life grand!?!&lt;/p&gt;
      &lt;/div&gt;
   &lt;/content&gt;
&lt;/entry&gt;</screen>
      </example>

      <para>Or do whatever they like with their favorite Atom Publishing
      Protocol client.</para>
    </section>

    <section>
      <title>Querying the Blog</title>

      <para>A service provider can query their blog to display the entries for
      a particular month:</para>

      <example>
        <title>Find Blog Entries</title>

        <screen>POST /atom/query/users/johndoe/blog HTTP/1.1
Host: example.org
User- Agent: Thingio/1.0
Content- Type: application/xquery
Content- Length: nnn

&lt;?xml version="1.0" ?&gt;
&lt;atom:feed xmlns:atom="http://www.w3.org/2005/Atom"&gt;
     {
         /atom:feed/atom:id,
         /atom:feed/atom:title,
         /atom:feed/atom:updated,
         for $e in (/atom:feed/atom:entry)
             where starts-with(string($e/atom:published),"2006-06")
                return ( "&amp;#x0a;",$e, "&amp;#x0a;")
     }
&lt;/atom:feed&gt;</screen>
      </example>
    </section>
  </chapter>

  <chapter>
    <title>Configuring the Atom Servlet</title>

    <section>
      <title>Enabling Atom Services.</title>

      <para>The Atom Servlet must be enabled to have Atom services provided by
      the standalone configuration. This is done by adding or enabled the Atom
      Servlet implementation in server.xml:<example>
          <title>Addition for server.xml</title>

          <programlisting>&lt;servlet enabled="yes"
         context="/atom/*"
         class="org.exist.atom.http.AtomServlet"/&gt;
</programlisting>
        </example></para>
    </section>

    <section>
      <title>Configuration</title>

      <para>By default, without any configuration file, all the atom modules
      are provided. If there is an atom-services.xml document in the eXist
      home directory, that configuration will be used instead. </para>

      <example>
        <title>atom-services.xml</title>

        <screen>&lt;atom-services xmlns="http://www.exist-db.org/Vocabulary/AtomConfiguration/2006/1/0"&gt;
&lt;module name="edit" class="org.exist.atom.modules.AtomProtocol"/&gt;
&lt;module name="content" class="org.exist.atom.modules.AtomFeeds"/&gt;
&lt;module name="query" query-by-post="true"/&gt;
&lt;module name="topic"&gt;
   &lt;method type="GET" query="/org/exist/atom/modules/topic.xq" from-classpath="true"/&gt;
&lt;/module&gt;
&lt;module name="introspect"&gt;
   &lt;method type="GET" query="/org/exist/atom/modules/introspect.xq" from-classpath="true"/&gt;
&lt;/module&gt;
&lt;module name="example"&gt;
   &lt;method type="GET" query="atom-example.xq"/&gt;
&lt;/module&gt;
&lt;/atom-services&gt;
</screen>
      </example>

      <section>
        <title>Adding a Custom Module</title>

        <para>A custom module, implemented in Java, can be configured by
        adding a
        <code>{http://www.exist-db.org/Vocabulary/AtomConfiguration/2006/1/0}module</code>
        element. The 'name' specifies the module name that will be used in the
        URL path and the 'class' attribute specifies the implementation class.
        This class must implement
        <code>com.exist.atom.AtomModule</code>.</para>
      </section>

      <section>
        <title>Adding a Query Module</title>

        <para>A query can be registered as module by adding a
        <code>{http://www.exist-db.org/Vocabulary/AtomConfiguration/2006/1/0}module</code>
        element without the 'class' attribute specitied. The 'name' specifies
        the module name that will be used in the URL path.</para>

        <para>As children of the element, a
        <code>{http://www.exist-db.org/Vocabulary/AtomConfiguration/2006/1/0}method</code>
        element can be specifed for each HTTP method for which a query should
        be associated. This element has three attributes: </para>

        <variablelist>
          <varlistentry>
            <term>type</term>

            <listitem>
              <para>The HTTP method to which the query should be
              associated--must be one of GET, PUT, POST, HEAD, or
              DELETE.</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term>query</term>

            <listitem>
              <para>A reference to the query source (a relative URL
              value).</para>
            </listitem>
          </varlistentry>

          <varlistentry>
            <term>from-classpath</term>

            <listitem>
              <para>A boolean value where 'true' indicates that the query
              should be loaded as a resource off the classpath.</para>
            </listitem>
          </varlistentry>
        </variablelist>
      </section>
    </section>
  </chapter>
</book>