I’ve found many examples of writing a Jersey @Provider to implement data marshalling, or mapping exceptions into a Response, but I've found very little on how to inject your own data types into your Jersey resource methods.
My code used to look something like this:
@POST@Path("/create")
@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)public Response createThing(@Context HttpServletRequest request, String arg) {
UserSession us = /* code to extract UserSession from cookie in request */
/* more code here */
}
But I wanted it to look like this:
@POST@Path("/create")
@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.APPLICATION_JSON)public Response createThing(@Context UserSession us, String arg) {
/* more code here */
}
Doing it this way cleans up the code more than the above snippets suggest. I eliminate some exception handling from all the resource functions that need the UserSession (which are many), plus I get a good separation of concerns. It’s just dependency injection.
So the @Provider class to support the new code looks like this:
import java.lang.reflect.Type;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
@Providerpublic class UserSessionProvider extends AbstractHttpContextInjectable<UserSession> implements InjectableProvider<Context, Type> {final static private Logger logger = LoggerFactory.getLogger(UserSessionProvider.class);@Overridepublic Injectable<UserSession> getInjectable(ComponentContext ic, Context a, Type c) {
if (c.equals(UserSession.class)) {return this;}return null;}@Overridepublic ComponentScope getScope() {
return ComponentScope.PerRequest;
}@Overridepublic UserSession getValue(HttpContext context) {
try {
// CookieUserSession takes care of the marshalling/validation of data in the cookie
CookieUserSession cookie = CookieUserSession.checkForCookie(context.getRequest().getCookies());return cookie.recoverAllFields(); // returns a UserSession, clearly}catch (CookieTamperedException e) {
logger.error(e.getMessage(), e);throw new RsException(e); // my subclass of WebApplicationException}}}
It took me a while to sort out which class I needed to extend (and interface to implement). A number of trials and errors gave me Jersey exceptions complaining that it could not find an injector for my resource function. Messages like this:
INFO: Initiating Jersey application, version 'Jersey: 1.14 09/09/2012 07:21 PM'Jan 02, 2013 10:20:12 PM com.sun.jersey.spi.inject.Errors processErrorMessagesSEVERE: The following errors and warnings have been detected with resource and/or provider classes:SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.example.rs.MyService.createThing(com.example.stateful.UserSession,java.lang.String) at parameter at index 0
SEVERE: Method, public javax.ws.rs.core.Response com.example.rs.MyService.createThing(com.example.stateful.UserSession,java.lang.String), annotated with POST of resource, class com.example.rs.MyService, is not recognized as valid resource method.
Hat tip to Antoine Vianey’s blog post where one of his examples demonstrated this.
No comments:
Post a Comment