Overview

This tutorial will guide you through the following concepts:

This assumes that you have some familiarity with Atom and AtomPub, but you will probably still get value out of the tutorial if you do not.

Concepts

In this tutorial we're going to walk through how to build an Atom Publishing Protocol service using Abdera's concepts of Providers and CollectionAdapters. If you remember your AtomPub basics, you'll recall that AtomPub services are organized in a hierarchical way such that we have:

Abdera provides some classes which map to these concepts:

AtomPub Concept

Abdera Classes

Description

Service

Provider

Providers provide the implementation of an AtomPub service. In general you should be able to just use the DefaultProvider which we will be using it as we construct our service.

Workspace

WorkspaceInfo & WorkspaceManager

The WorkspaceInfo class provides metadata about your workspace for the services document. The WorkspaceManager provides a way for you to list out the workspaces in a service.

Collection

CollectionAdapter

A CollectionAdapter is where your business logic will lie and allows you to implement the basic GET/DELETE/POST/etc operations for an AtomPub Collection.

Minimal required Maven dependency

xml <dependency> <groupId>org.apache.abdera</groupId> <artifactId>abdera-server</artifactId> <version>1.1.1</version> </dependency>

Writing your CollectionAdapter

Abdera comes with a class called the AbstractEntityCollectionAdapter. This class provides a simple "fill in the blanks" approach so that you can easily map your concepts to an AtomPub collection. The idea is that you have an entity class which represents each individual entry. For instance, if you were writing a blog, you would have a BlogEntry class which backs the entry. Or in our case of an employee directory, it would be an Employee class. Abdera will then provide some template methods which you fill in to give Abdera the necessary metadata.

Our Atom Collection is going to be a simple store of employees which can be added to, deleted from, updated, etc. To get started, we must first build our Employee class:

{snippet:id=employee|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/Employee.java}

The ID is going to be used as a unique identifier for our Employee so that even if the employee's name changes, we can tie it to a previous atom entry and track the changes. The updated date will be used for our Atom feed, so that consumers know if there were changes and when they occurred.

Now we need to create our CollectionAdapter:

java import org.apache.abdera.protocol.server.impl.AbstractEntityCollectionAdapter; public class EmployeeCollectionAdapter extends AbstractEntityCollectionAdapter<Employee> { ... }

The first thing we'll do is provide a hashmap to store our Employees in. Typically this will be a database or some other type of backend store. To keep things simple, we're going to store employees in a Map according to their employee id.

java private Map<Integer, Employee> employees = new HashMap<Integer, Employee>();

Next up, we need to implement some methods which provide some basic metadata about our collection such as the feed author, the feed ID, and the feed title:

{snippet:id=feedmetadata|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/EmployeeCollectionAdapter.java}

The ID in getId(RequestContext) must be a unique identifier for your feed. The idea is that even if your feed location changes, or someone is redistributing your feed, the feed reader can use this ID to identify that two feeds are the exact same. For information on how to construct feed IDs and why they matter, its recommend that you use this article on how to create feed ids.

Providing Entry Metadata

The next step is to implement the methods which provide some basic metadata about our entries:

{snippet:id=entryMetadata|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/EmployeeCollectionAdapter.java}

Walking through this we have:

Mapping Entries to Resources

The AbstractEntityCollectionAdapter maps individual entries via a "resource name." You must provide a way to create a resource name for your entry and also provide a way to look up an entry from your resource name. In this case, our entry is an Employee.

When we do this we must ensure that we won't have conflicts. Which means we don't want to use the employee name as the unique resource name as there may be conflicts. However we don't necessarily want to use just the employee ID either as the URL isn't very friendly. So we'll create a hybrid of the form: "EMPLOYEE_ID-EMPLOYEE_NAME".

java public String getName(Employee entry) { return entry.getId() + "-" + Sanitizer.sanitize(entry.getName()); }

Note the use of the sanitizer. This will take any invalid characters for a URL and either strip or replace them from the string.

We must also provide a way to turn a resource name into an employee:

{snippet:id=getEntry|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/EmployeeCollectionAdapter.java}

Implementing POST/DELETE/PUT

The last step is to implement the POST, DELETE, and PUT operations.

{snippet:id=methods|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/EmployeeCollectionAdapter.java}

The POST method corresponds to creating an Employee in the database. Here we're:

The putEntry method is similar. In this case we're just updating the employee name.

Finally, we have the deleteEntry method which gives us the resource name and allows us to remove it from the Map.

What about HEAD?

Because we've already supplied Abdera with lots of metadata about our entry, you get the HEAD operation for free!

Setting up your Provider and the URL space

You've now written an Abdera CollectionAdapter to expose your employee database to the world via an Atom collection. You must still expose it via a servlet though. Below shows how to extend the AbderaServlet to create your provider.

{snippet:id=servlet|lang=java|javadoc=false|linenumbers=true|url=abdera/trunk/examples/src/main/java/org/apache/abdera/examples/appserver/employee/AppServer.java}

In this code we're setting up a Provider which contains an Atom workspace which contains our Atom collection.

When we initialize our CollectionAdapter we call setHref. Abdera uses this to determine the URL space. This leaves us with the following URL structure:

URL

Description

/

The services document

/employee

The Atom feed which maps to the employee collection.

/employee/XXX

An individual atom entry from our collection.

You could just as well use the Spring Integration or the web.xml configuration capabilities of Abdera to expose your Provider/CollectionAdapter as well.

Deploying your service

Finally, you can deploy your Atom service inside your favorite servlet container witht he following XML.

<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet id="abdera"> <servlet-name>Abdera</servlet-name> <servlet-class>org.apache.abdera.example.EmployeeProviderServlet</servlet-class> </servlet> <servlet-mapping id="abdera-mapping"> <servlet-name>Abdera</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>