Implementing A Server

Contributed by: Adam Constabaris

Table of Contents

Introduction
Implementing Provider
Putting it all together

Introduction

The framework for implementing an APP server in Abdera is, as shipped, incomplete, but armed with a knowledge of how APP works, you shouldn't have too much trouble enabling APP access to an (appropriate!) domain model. In order to effectively use the classes in the framework, you'll have to know how to configure them and what you need to implement yourself. The first part of the document describes (loosely) how Abdera is configured, while the second part concerns notes about implementing some of the more important classes. I assume throughout that you are familiar with the Atom format, APP basics, and Abdera's general model for representing the Atom format.

Abdera is designed to use a controller servlet, and ships with a basic implementation that is be suitable for a range of situations. Changing your Abdera server configuration is largely achieved by setting properties of the form some.abdera.framework.Interface=my.domain.abdera.InterfaceImplementation; that is, the property's name is the fully qualified name of the interface whose implementation you want to specify, and the value is the fully-qualified name of a class that implements that interface. Swapping out Abdera's default parser factory for a SAX-based one, for example, would be done by setting org.apache.abdera.parser.ParserFactory to (say) net.my.domain.MyParserFactory.

Properties can be specified in a number of places; first, most of the crucial bits of core Abdera are specified in org.apache.abdera.util.Constants. You can also use the Java standard service provider mechanism, or a file named abdera.properties on the classpath (generally, values are looked up in this order, with later values overriding ones set by earlier mechanisms). While Abdera core ships with settings that allow you to parse or create feed documents without any special configuration, server implementations are more variable and so require more work.

As you might expect, this form of configuration makes heavy use of reflection, and default or well-known constructors. In general, if you don't need to supply a lot of helper classes or are happy using (e.g) JNDI lookup mechanisms to look up such things as database connections, you can use these mechanisms without too much fuss. It's also not much more work to use a dependency injection framework (such as Spring) for your server configuration.

The interfaces you need to implement in order to create an APP server are listed in org.apache.abdera.protocol.server.util.ServerConstants:

org.apache.abdera.protocol.server.servlet.RequestHandlerManager

Factory for objects that control the request processing.

org.apache.abdera.protocol.server.auth.SubjectResolver

Factory for objects that determine the identity of calling clients.

org.apache.abdera.protocol.server.provider.TargetResolver

Factory for objects that determine the type of APP entity involved in the request (e.g. entry, collection, media, service, categories)

org.apache.abdera.protocol.server.ServiceContext

Controller: coordinates all of the other factory classes.

org.apache.abdera.protocol.server.provider.ProviderManager

Factory for Provider implementations that implement the CRUD operations of the APP server.

The org.apache.abdera.protocol.server.provider.Provider interface defines all of the CRUD operations involved in the Atom protocol: this is where you put the code that gets entries, collections, service documents,and handles updates and deletions; most of the rest of these classes are involved in making sure an HTTP request is routed to the right method on the Provider. Since the ProviderManager class is responsible for instantiating and configuring your Provider objects, it has an important role to play. Concrete default implementations for most of the other interfaces are provided, but you will have to provide implementations for both ProviderManager and Provider.

An absolutely minimal APP server will also have to configure a target resolver, i.e. provide a way to determine which kind of APP entity is being operated on (i.e. it answers questions of the form "is this a request for a service document?"). In all likelihood, a subclass of org.apache.abdera.protocol.server.util.RegexTargetResolver will suffice here, since it maps request URIs that match a particular regular expression to entity types. Your subclass can supply the actual mapping that you're going to use (see SimpleTargetResolver in the server example). Additionally, since the ServiceContext is responsible for getting references to all these other classes and configuring them (most importantly the ProviderManager), you'll probably want to subclass it as well.

Implementing Provider

While the specifics are going to be largely determined by the type of information your server is processing and how it's stored (e.g. on a file system vs. rows in a database), there are a few things to look out for that apply to any Provider implementation. One thing you'll definitely need is need a system for turning objects in your domain model into Abdera's model objects, and vice-versa. This part has to be left as an exercise for the reader.

Most of the methods in the Provider interface are fairly well described by their names: getEntry, for example, is called when the client has called GET on an entry URI. One exception here is the getInfo method, which serves mainly to enable support for HTTP's conditional GET [ see also ].

You'll notice that the various getXXX methods return a ResponseInfo object, which encompasses the HTTP response, including the headers and response body. In addition to the RequestContext argument, these methods also accept a boolean argument that determines whether the method is evaluating the request for cacheability[1] or whether it should actually generate a response body. Let's refer to the first mode (where the boolean parameter is false) as "header mode" and the second as "full response mode". When the method is called in header mode, it may check the ETag or If-Modified-Since HTTP headers submitted by the APP client against the entry's current tag or modification time.

So, if you're implementing conditional GET in your Provider, the getInfo method can delegate to the appropriate getXXX method in header mode. Even if your provider doesn't support conditional GET, you'll probably want to support different header and full response modes, to avoid having to generate a potentially "expensive" document twice. A relatively straightforward way to do this is to have your methods return an EmptyResponseContext with a status code of 200 (or 404, if the user's requested an entry that doesn't exist) when they're called in header mode. The default request processing pipeline ensures that the method will be called again in full response mode.

Putting it all together

The default AbderaServlet class can be used as your servlet controller, but it is somewhat limited in how much "help" it can provide by way of making servlet or EJB container resources such as database connection pools available to your protocol implementation. AbderaServlet uses the property-resolution mechanisms described above[2]. The basic steps it uses to process a request are:

  1. Obtain a reference to a ServiceContext (via reflection).

  2. Use the service context to get a reference to a RequestHandlerManager.

  3. Use the request handler manager to obtain a RequestHandler.

  4. Call process() on the request handler.

If your ServiceContext implementation can get references to appropriately configured target resolvers and provider managers, then all you need to do is map the AbderaServlet to the desired URI and set the org.apache.abdera.protocol.server.ServiceContext property to the name of your service context implementation.

If you require greater control over how the ServiceContext is instantiated or configured, you should create your own servlet whose service method executes this basic process.



[1] They can also check that certain preconditions are satisfied, e.g. the "lost update" mechanism for a PUT (update) request, which checks whether the ETag supplied by the client matches the current ETag for the associated entry, but I'll leave that part out.

[2] With one twist: it also lets you specify your implementation classes via the servlet's initialization parameters.