Latest Posts

Archives [+]

Playing with AtomPub on CRX

Apache Abdera is an Apache Software Foundation project to...

build a functionally-complete, high-performance implementation of the IETF Atom Syndication Format (RFC 4287) and Atom Publishing Protocol (RFC 5023) specifications.

(if reading RFCs is not your cup of tea: IBM developerworks has some good tutorials on AtomPub. Update: Julian Reschke pointed me to these HTML versions of the Publishing and Syndication spec which are quite a bit easier on the eyes).

The project has recently graduated from the incubator and is now a top level project at the ASF. Congratulations!

Since release 0.4 Abdera comes with a JCR adapter (JCR-loves-Atom, remember?). The JCR adapter allows you to run an Abdera AtomPub server and have a Jackrabbit repository be used for storage.

Getting it to run is quite easy: do a checkout of the 0.4 release at http://svn.apache.org/repos/asf/abdera/java/tags/abdera-0.4.0-incubating/ and built with

mvn clean install

This command will also execute the test cases in adapters/jcr/src/test/java/org/apache/abdera/jcr. As part of the test cases a JCR-backed Atom server gets started and some JCR nodes are created through Atom. The main magic happens in the JcrCollectionProvider class that maps the AtomPub elements onto JCR nodes. For example, AtomPub's ID is mapped onto JCR's UUID:

public String getId(Node entry) throws ResponseContextException {
 try {
  return "urn:" + entry.getUUID();
 } catch (RepositoryException e) {
  throw new ResponseContextException(500, e);
 }
}

Getting Abdera to interact with CRX

The primary purpose of the JcrCollectionAdapter class is to equip a stand-alone Atom server with a JCR repository for storage. However, with a bit of tweaking the class can also be used to provide an Atom interface to an existing CRX repository: a simple way to get things running is to leave the existing CRX Quickstart untouched and connect to the repository through RMI. RMI is disabled by default, but on CRX's Knowledge Base is an article how to enable it.

Abdera bundles Jetty which can be used to provide the http server for AtomPub. I have wrapped the JcrCollectionProvider in a little servlet and initialized Jetty with this servlet. The class looks like this:

import org.apache.jackrabbit.rmi.client.ClientRepositoryFactory;
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.servlet.http.HttpServlet;
import org.apache.abdera.protocol.server.Provider;
import org.apache.abdera.protocol.server.impl.DefaultProvider;
import org.apache.abdera.protocol.server.impl.SimpleWorkspaceInfo;
import org.apache.abdera.protocol.server.servlet.AbderaServlet;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;

public class AppServer {

 public static void main(String... args) throws Exception {
  int port = 9002;
  try {
   port = args.length > 0 ? Integer.parseInt(args[0]) : 9002;
  } catch (Exception e) {
  }
  Server server = new Server(port);
  Context context = new Context(server, "/", Context.SESSIONS);
  ServletHolder servletHolder = new ServletHolder(new
  JcrCollectionServlet());
  context.addServlet(servletHolder, "/*");
  server.start();
  server.join();
 }

 public static final class JcrCollectionServlet extends AbderaServlet {
  protected Provider createProvider() {

   DefaultProvider jcrProvider = new DefaultProvider();

   try {

    ClientRepositoryFactory factory = new ClientRepositoryFactory();
    Repository repository = factory.getRepository("//localhost:1234/crx");

    JcrCollectionAdapter cp = new JcrCollectionAdapter();
    cp.setTitle("My Entries");
    cp.setAuthor("Apache Abdera");
    cp.setCollectionNodePath("entries");
    cp.setRepository(repository);
    cp.setCredentials(new SimpleCredentials("admin", "admin".toCharArray()));
    cp.setHref("feed");
    cp.initialize();

    SimpleWorkspaceInfo wkspc = new SimpleWorkspaceInfo();
    wkspc.setTitle("JCR Workspace");
    wkspc.addCollection(cp);
    jcrProvider.addWorkspace(wkspc);
    jcrProvider.init(getAbdera(), getProperties(getServletConfig()));
   } catch (Exception e) {
    return null;
   }
   return jcrProvider;
  }
 }
}

Please note that the servlet just needs to acquire the JCR and pass a reference to it to the JcrCollectionProvider. Thus, this approach can be used with JNDI or other means of connection as well and is not specific to RMI. Before you run the server you will have to perform a little tweak in the JcrCollectionProvider: when the provider is started it will try to register a new node type. AFAIK this does not work via RMI (I might be wrong, though), so I commented this code in the initialize() method :

//JackrabbitNodeTypeManager jntmgr = (JackrabbitNodeTypeManager) workspace.getNodeTypeManager();
//if (!jntmgr.hasNodeType("abdera:entry")) {
 //InputStream in = getClass().getResourceAsStream("/org/apache/abdera/jcr/nodeTypes.xml");
 //try {
  //jntmgr.registerNodeTypes(in, JackrabbitNodeTypeManager.TEXT_XML);
 //} finally {
  //in.close();
 //}
//}

and manually added the the namespace abdera="http://abdera.apache.org" and the node type "abdera:entry" defined in adapters/jcr/src/main/resources/org/apache/abdera/jcr/nodeTypes.xml to CRX (find a link to the node type manager that provides a UI for registering namespaces and node types at http://localhost:7402/crx). The JcrCollectionProvider will use its specific node type abdera:entry to store and retrieve data.

After you have defined abdera:entry run the AtomPub server main class from above and find your Atom interface at http://localhost:9002/feed (make sure to include the CRX RMI jars from crx-quickstart/server/runtime/0/_crx/WEB-INF/lib on your classpath). You create nodes via curl (see here for an example) or (more or less convenient, depending on your taste) via the AbderaClient class like this code below. It creates a new post with some example data:

import java.util.Date;
import javax.jcr.Repository;
import org.apache.abdera.Abdera;
import org.apache.abdera.factory.Factory;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.model.Entry;
import org.apache.abdera.protocol.client.AbderaClient;
import org.apache.abdera.protocol.client.RequestOptions;
import org.apache.abdera.protocol.server.impl.DefaultProvider;

// this code is taken from Dan Diephous's
// test classes for the JcrCollectionProvider 
public class AppServerTest {

 private DefaultProvider jcrProvider;
 private Repository repository;

 public static void main(String... args) throws Exception {
  Abdera abdera = new Abdera();
  Factory factory = abdera.getFactory();

  AbderaClient client = new AbderaClient(abdera);

  String base = "http://localhost:9002/";

  // Testing of entry creation
  IRI colUri = new IRI(base).resolve("feed");
  Entry entry = factory.newEntry();
  entry.setTitle("Some Entry");
  entry.setUpdated(new Date());
  entry.addAuthor("Dan Diephouse"); // thanks, for the provider, Dan!
  entry.setId(factory.newUuidUri());
  entry.setSummary("This is my entry.");
  entry.setContent("This is my entry. It's swell.");

  RequestOptions opts = new RequestOptions();
  opts.setContentType("application/atom+xml;type=entry");
  ClientResponse res = client.post(colUri.toString(), entry, opts);
 }
}

(the example is taken from Dan Diephouse's test classes for the JcrCollectionProvider)

This might be your first step if you want to offer an AtomPub interface to your CRX repository. However, it is likely that you will have to tweak the JcrCollectionProvider class a bit more: the crucial part is the mapping between JCR nodes and AtomPub elements. As discussed above the out-of-the-box JcrCollectionProvider can only deal with nodes of type abdera:entry. However, this will not suffice to Atom-enable an existing application, the mapping needs to fit to your specific application's node types.

Somewhat related to AtomPub and JCR: Apache Jackrabbit is expected to support CMIS in the future (which includes an AtomPub binding). Work has started on this and there is some initial code in the sandbox.

 

COMMENTS

  • By Mohan Radhakrishnan - 2:01 PM on Aug 30, 2010   Reply
    How important is CMIS in the content management space ? If JCR is concerned with uniform repository design, CMIS is concerned with exposing it to the outside world using web services ? Is this definition correct ?