Table of Contents
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.
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.
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:
Obtain a reference to a ServiceContext
(via reflection).
Use the service context to get a reference to a RequestHandlerManager
.
Use the request handler manager to obtain a RequestHandler
.
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.