16 May 2009
Ok, let’s get back to the geeky stuff.
Another interesting thing about Jersey (in addition to parameter classes) is the way it uses dependency injection.
Jersey doesn’t have an abstract base class for resources, like Rails’
ActionController::Base or Restlet’s
Resource. This removes the obvious way
of retrieving information about the incoming request: from the base class.
Take this Rails controller action, for example:
request is a method defined on
ActionController::Base which returns an
object encapsulating the information about the current HTTP request.
But how would you test this action in isolation? Hopefully the base class provides an easy way of passing in a mock request object, or else you’re stuck modifying instance variables or partially mocking the class you’re trying to test.
Jersey avoids this situation by injecting the required information into the resource class:
Jersey detects the
@Context annotation and automatically injects a
with the current request’s data into the method. You can do this for
SecurityContext, and a few other Jersey classes.
When it comes time to test this class, it’s a simple matter of making a mock
UriInfo and passing it in:
Because the resource class doesn’t go out and get the
UriInfo itself, it’s
much easier to test.
What takes this feature from Neat to Indispensable is the fact that Jersey opens this infrastructure to you: it’s easy to write your own injection providers to peel arbitrary bits of an HTTP request off and inject them into your resources.
As an easy example, let’s take the request’s locale, as determined by its
Accept-Language header. Out of the box, Jersey gives us access to this via
HttpHeaders#getAcceptableLanguages(), which returns a list of
instances in order of preference.
But it’s a tedious thing to have our resource class have an
instance injected and then get the first item from the
In order to test that, we’ll have to come up with an
HttpHeaders mock and stub
getAcceptableLanguages() to return a list of locales. Bleagh.
In keeping with my “so what if it looks good on a slide” motif here, I’m also going to throw in error handling: what if the user doesn’t specify a locale?
That’s goddamn horrible–we’ll have to test that logic all over the place,
lest we end up throwing an
IndexOutOfBoundsException because someone’s HTTP
client has funny ideas about valid locales are.
It would be nice to have the locale-selecting code in its own class, and to do
that in the same way that our
HttpHeaders instance was injected.
In order to do that we’ll need to write two things:
Injectable<Locale>, which will be responsible for extracting a
Localefrom an HTTP request context.
InjectableProvider<Locale>, which will be responsible for injecting instances of #1.
Jersey has a specific class to handle the first responsibility:
AbstractHttpContextInjectable which is used to inject information from the
HTTP context into resource classes. The second responsibility is simple enough
to not require a template base class.
Luckily for us, we can kill two birds with one stone and implement both complimentary responsibilities in a single class:
This is kind of a complicated class. Let’s cover a few things.
@Providerannotation marks it so that Jersey will add it as an injection provider.
getInjectablemethod checks to see that
Locale–that is, if Jersey is asking this provider if it can inject a
Locale. If so, it returns itself to do the injection. Otherwise, it returns
nullto indicate there’s nothing it can inject.
getScopemethod indicates that the returned injectable is only meaningful on a per-request basis.
getValuemethod is where the real logic happens. Once the
LocaleProviderinstance has been returned from
getInjectable, Jersey passes it an
HttpContext, which contains all the relevant information about the HTTP request. It returns the most-preferred locale, including error handling.
When we put
LocaleProvider in a package that Jersey’s configured to scan, we
can reduce our resource class logic to this:
Then our test looks like this:
But I don’t think we’re done yet.
Much like parameter classes, our code gets cleaner the more injection providers we write, so we need to extract out the guts into a base class:
Now our provider is sleek and shiny:
Now both our resource class and our
LocaleProvider are composed and testable.
I love it when a plan comes together.
Jersey has an internal dependency injection system which allows you to write small, focused classes to extract aspects of an HTTP request–in our case, the request’s locale–and inject them into your resource classes as an object of an appropriate type. This makes for smaller, more composed, more testable resource classes, which in turn makes for an application which is easier to change.