<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: atompub.xml 17299 2012-10-10 23:47:33Z joewiz $ --><book><bookinfo><productname>eXist-db – Open Source Native XML Database</productname><title>Atom Publishing Protocol Support</title><date>September 2009</date><author><surname>Milowski</surname><firstname>Alex</firstname><email>alexmilowski at users.sourceforge.net</email></author></bookinfo><chapter><title>A Short Introduction to the Atom Publishing Protocol</title><section><title>Introduction</title><para>The <ulink url="http://www.ietf.org/rfc/rfc5023.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 Atom
              Publishing Format and Protocol 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. Though Atom is often associated
          with newsfeeds, it can be used for a much wider range of applications.</para><para>eXist-db provides an implementation of the <ulink url="http://www.ietf.org/rfc/rfc5023.txt">Atom
              Publishing Protocol</ulink> sitting on top of the database. Feed and entry
              data is stored into special collections. Our wiki and blog system,
              <ulink url="http://code.google.com/p/atomicwiki">AtomicWiki</ulink>, which 
              powers the <ulink url="http://atomic.exist-db.org/blogs/eXist/">eXist Wiki</ulink>
              is completely based on eXist's Atom support.</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><note><para>It is important to specify the correct Content-Type in the HTTP header.
		If the content type is not application/atom+xml, the server will just
		store the resource as a document into the existing feed collection and
		add an entry to the feed which links to the new resource.</para></note><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><programlisting><markup>&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;</markup></programlisting></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><section><title>Setting Permissions</title><para>When creating a feed or entry, you can specify which permissions should
              be applied to the newly created resource within eXist. For this, the entry
              or feed document should contain an empty element, <sgmltag>exist:permissions</sgmltag>,
              in the "exist" namespace.</para><synopsis language="xml">&lt;exist:permissions xmlns:exist="http://exist.sourceforge.net/NS/exist"
              owner="myuser" group="mygroup" mode="755"/&gt;</synopsis><para>where <option>owner</option> and <option>group</option> should be valid database
              users/groups and <option>mode</option> is an octal number defining the permissions
              to be set. Please refer to the <ulink url="security.xml#octal">Security Guide</ulink>
              for further information.</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 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 sent:</para><example><title>A Feed Query</title><programlisting><markup>&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;</markup></programlisting></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><markup>&lt;servlet enabled="yes"
         context="/atom/*"
         class="org.exist.atom.http.AtomServlet"/&gt;</markup></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><programlisting><markup>&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;</markup></programlisting></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 specified. 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><chapter><title>Extensions</title><section><title>Setting permissions when creating feeds/entries</title><para>When creating a new feed or entry, you can specify
                access permissions as well as an owner and owner group
                for the new resource. This is done by including a special
                <sgmltag>exist:permissions</sgmltag> element in the posted
                feed or entry document:</para><synopsis>&lt;exist:permissions mode="0755" owner="editor" group="users"/&gt;</synopsis><para>The <option>mode</option> attribute specifies the permissions to be
                assigned to the resource. The attribute value has to be an octal number
                or a string describing the permissions to set, using the syntax:</para><synopsis>[user|group|other]=[+|-][read|write|update][, ...]</synopsis><para>The <option>owner</option> attribute specifies the user which will own
                all resources associated to the feed or entry. Parameter <option>group</option>
                specifies the user group.</para></section></chapter></book>