Atom support

Project Zero provides Atom support both implicitly and explicitly. This article describes the explicit support that is available in the zero.atom extension.

Project Zero provides the following types of Atom support:

  • Implicit support in the Zero Resource Model model-driven development
  • Explicit support with Apache Abdera and an Atom renderer for simplified serialization

It is the explicit support, available with the zero.atom extension, that is discussed in this article.

Adding zero.atom to your application

To add zero.atom to your application, start by adding the following line to the dependencies element in your config/ivy.xml file:

       <dependency org="zero" name="zero.atom" rev="1.0+"/> 

Abdera supports XPath as an alternative to Feed Object Model navigation, but zero.atom does not include XPath support for Abdera. You can add XPath support using the standard Abdera mechanisms.

Output handling: Atom renderer

The Atom renderer simplifies serialization to Atom entry, feed, and service documents. Using a simple convention, the renderer serializes Maps to entries and Map arrays and List<Map> to feeds. The renderer also serializes Abdera elements (Entry, Feed, and Service) for consistency.

The general syntax for invoking the Atom renderer within Groovy scripts is similar to the core renderers, as shown in the following example:

 request.view = 'atom'
 request.atom.output = object
 render()

The following sections describe the specific behavior for various object types.

Rendering a Map as an Entry

A simple convention is used to convert a Map to an entry. This is shown in the following sections that provide several examples in Groovy. An equivalent example in PHP is shown in a later example.

Example: Rendering an Entry

The following example shows an entry rendered for a Groovy-based resource handler:

 def onRetrieve(){
 entry = [
  // Required
  id : '1',
  title : 'title',
  updated : new Date(0),
  authorname : 'name',
  content : 'content',
  contenttype : 'TEXT',
    
  // Optional
  categorylabel : 'label',
  categoryterm : 'term',
  summary : 'summary',
  summarytype : 'TEXT',
     
  // Extensions
  f1 : 'v1'
 ]
 request.view = 'atom'
 request.atom.output = entry
 render()
 } 

The following Atom entry document is the result of the previous example:

<?xml version="1.0" encoding="UTF-8"?> 
<entry xmlns:zero="http://projectzero.com/namespace" xmlns="http://www.w3.org/2005/Atom">
  <id>http://localhost:8081/resources/custom/1</id>
  <link href="http://localhost:8081/resources/custom/1" rel="edit"></link>
  <title type="text">title</title>
  <updated>1970-01-01T00:00:00.000Z</updated>
  <author><name>name</name></author>
  <content type="text">content</content>
  <summary type="text">summary</summary>
  <category term="term" label="label"></category>
  <zero:f1 xmlns:zero="http://projectzero.com/namespace">v1</zero:f1>
</entry> 

Example: Custom feed URI

By default, the request URI is used to construct the id and link values. The convention assumes that the request URI is of the form <feedURI>/{id} , which is probably correct in cases when dealing with resource handlers. In some situations (for example, indirect access from a script in the public folder), you can specify a value for <feedURI> by setting the /request/atom/feedUri property for the renderer, as shown in following Groovy syntax:

 request.view = 'atom'
 request.atom.output = textEntry
 request.atom.feedUri = 'http://other.host.com/custom' 

The following Atom entry document is the result of the previous example:

 <?xml version="1.0" encoding="UTF-8"?>
 <entry xmlns:zero="http://projectzero.com/namespace"
xmlns="http://www.w3.org/2005/Atom">
  <id>http://other.host.com/custom/1</id>
  <link href="http://other.host.com/custom/1" rel="edit"></link>
  <title type="text">title</title>
  <updated>1970-01-01T00:00:00.000Z</updated>
  <author><name>name</name></author>
  <content type="text">content</content>
  <summary type="text">summary</summary>
  <category term="term" label="label"></category>
  <zero:f1 xmlns:zero="http://projectzero.com/namespace">v1</zero:f1>
</entry> 

Details about the entry conventions

Instances of Map are serialized by first forming equivalent Abdera Entry types, based on the mappings of properties to Atom elements shown in the following table.

Required properties
Map Key Expected Data Type Atom element Contents
id int or String atom:id uri: http://{hostname:port}/... {requestUri}/{id}
atom:link uri: http://{hostname:port}/... {requestUri}/{id} (rel=edit)
title String atom:title text: {title}
authorname String atom:name of atom:author text: {authorname}
updated Date atom:updated date: {updated}
contenttype String ("TEXT" or "HTML" or "XHTML") atom:type of atom:content text: {contenttype}
content String atom:content text: {content}
Optional properties
Map Key Expected Data Type Atom element Contents
categoryterm String atom:term of atom:category text: {categoryterm}
categorylabel String atom:label of atom:category text: {categorylabel}
summary String atom:summary text: {summary}
summarytype String ("TEXT" or "HTML" or "XHTML") atom:type of atom:content text: {summarytype}
Extensions
Map Key Expected Data Type Atom element Contents
{key} Object simpleExtensionElement text: {object}.toString()

The Atom simpleExtensionElement XML element is appended to the Atom entry with the following characteristics:

element name
{key}
element namespace
"http://projectzero.com/namespace"
element name prefix
"zero"
element value:
{object}.toString

Rendering a List<Map> or Map array as a feed

A simple convention is used to convert List<Map> and Map arrays to a feed, as the examples in the following sections show.

Example: Rendering a feed

The following example shows a Groovy-based resource handler:

 def onList() {
   entry = [
    // Required
    id : '1',
    title : 'title',
    updated : new Date(0),
    authorname : 'name',
    content : 'content',
    contenttype : 'TEXT'
  ]

   request.view = 'atom'
   request.atom.output = [entry]
   render()
  } 

The following example shows the Atom feed document that is the result of the previous example.

 <?xml version="1.0" encoding="UTF-8"?>
 <feed xmlns="http://www.w3.org/2005/Atom">
  <id>http://localhost:8081/resources/custom</id>
  <title type="text">http://localhost:8081/resources/custom</title>
  <link href="http://localhost:8081/resources/custom" rel="self"></link>
  <updated>1970-01-01T00:00:00.000Z</updated>
  <entry>
    <id>http://localhost:8081/resources/custom/1</id>
    <title type="text">title</title>
    <updated>1970-01-01T00:00:00.000Z</updated>
    <author><name>name</name></author>
    <content type="text">content</content>
    <link href="http://localhost:8081/resources/custom/1" rel="edit"></link>
  </entry>
 </feed> 

Example: Custom feed URI and title

The convention assumes that the request URI for a feed is the feed URI, which is used to construct entry elements (id and link) and feed elements (id, link, and title). This is probably suitable for cases involving resource handlers, but might not be correct for situations involving indirect access (for example, a resource served optionally using a script in the public folder). In such cases, you can specify the feed URI and title directly with additional properties to the renderer, as shown in the following Groovy syntax:

 request.view = 'atom'
 request.atom.output = textEntry
 request.atom.feedUri = 'http://other.host.com/custom'
 request.atom.feedTitle = 'my feed' 
 <?xml version="1.0" encoding="UTF-8"?>
 <feed xmlns="http://www.w3.org/2005/Atom">
   <id>http://other.host.com/custom</id>
   <title type="text">my feed</title>
   <link href="http://other.host.com/custom" rel="self"></link>
   <updated>1970-01-01T00:00:00.000Z</updated>
   <entry>
     <id>http://other.host.com/custom/1</id>
     <title type="text">title</title>
     <updated>1970-01-01T00:00:00.000Z</updated>
     <author><name>name</name></author>
     <content type="text">content</content>
     <link href="http://other.host.com/custom/1" rel="edit"></link> 
  </entry>
 </feed> 

Details about the feed conventions

Instances of List<Map> and Map arrays are serialized in two steps. The first step is to form an Entry instance for each Map instance. The Feed instance is then the collection of the Entry objects and additional feed elements shown in the following table.

Atom element Contents
atom:id uri: http://{hostname:port}/{requestUri}
atom:link uri: http://{hostname:port}/{requestUri} (rel=self)
atom:title text: "http://{hostname:port}/{requestUri}"
atom:updated most recent atom:updated value from entry list

Input handling: Parsing Atom entry

Use the Abdera APIs to parse an incoming Atom entry (for example, in a POST body). This is shown in the following Groovy snippet example:

 import org.apache.abdera.Abdera;
 ...
 Entry entry = Abdera.getNewParser().parse(request.input[]).getRoot();

The following example is a Java snippet:

 import java.io.InputStream;
 import org.apache.abdera.Abdera;
 ...
 InputStream is = GlobalContext.<InputStream>zget("/request/input");
 Entry entry = Abdera.getNewParser().parse(is).getRoot(); 

Atom handling in PHP

This section describes how to render Atom feeds and entries from your PHP script.

The PHP environment supports Atom by leveraging the IBM® WebSphere® sMash support for Atom.

Enabling PHP

To enable Atom/PHP support, add the following dependencies to the ivy.xml file.

zero.php
zero.atom

Atom renderer

The following code samples demonstrate the rendering of Atom entry and feed documents. Notice that two distinct types of date values are accepted in the updated field.

  1. Timestamp : This is a numeric value representing the number of milliseconds since the Unix epoch, which is defined as January 1 1970 00:00:00 GMT. Note that the time() function returns the number of seconds since the epoch.
  2. Date String : This is a string value representing a date. Acceptable values may include the date only ("1970-01-01") or the date and time ("1980-12-25 12:00:00"). For millisecond resolution, the Timestamp format should be used.

Atom entry document

<?php
$entry = array(
	"id" => 1,
	"title" => "A Good Title is Important",
	"authorname" => "John Doe",
	"updated" => time() * 1000, // milliseconds since the epoch
	"contenttype" => "TEXT",
	"content" => "Content is also important."
);

zput("/request/view","atom");
zput("/request/atom/output",$entry);
render_view();
?>

Atom feed document

<?php

// Rendering an Atom feed document.
$feed = array(
array(
"id" => 1,
"title" => "A Good Title is Important",
"authorname" => "John Doe",
"updated" => "1970-01-01", // date format
"contenttype" => "TEXT",
"content" => "Content is also important."
),
array(
"id" => 2,
"title" => "Bad Titles are Misleading",
"authorname" => "Jane Q. Sample",
"updated" => "1980-12-25 12:00:00", // date time format
"contenttype" => "TEXT",
"content" => "Content is also important."
)
);
zput("/request/view","atom");
zput("/request/atom/output",$feed);
render_view();

?>

Version 1.1.0.0.21442