Tutorial :Using @Context, @Provider and ContextResolver in JAX-RS



Question:

I'm just getting acquainted with implementing REST web services in Java using JAX-RS and I ran into the following problem. One of my resource classes requires access to a storage backend, which is abstracted away behind a StorageEngine interface. I would like to inject the current StorageEngine instance into the resource class serving the REST requests and I thought a nice way of doing this would be by using the @Context annotation and an appropriate ContextResolver class. This is what I have so far:

In MyResource.java:

class MyResource {      @Context StorageEngine storage;      [...]  }  

In StorageEngineProvider.java:

@Provider  class StorageEngineProvider implements ContextResolver<StorageEngine> {      private StorageEngine storage = new InMemoryStorageEngine();        public StorageEngine getContext(Class<?> type) {          if (type.equals(StorageEngine.class))              return storage;          return null;      }  }  

I'm using com.sun.jersey.api.core.PackagesResourceConfig to discover the providers and the resource classes automatically, and according to the logs, it picks up the StorageEngineProvider class nicely (timestamps and unnecessary stuff left out intentionally):

INFO: Root resource classes found:      class MyResource  INFO: Provider classes found:      class StorageEngineProvider  

However, the value of storage in my resource class is always null - neither the constructor of StorageEngineProvider nor its getContext method is called by Jersey, ever. What am I doing wrong here?


Solution:1

I don't think there's a JAX-RS specific way to do what you want. The closest would be to do:

@Path("/something/")  class MyResource {      @Context      javax.ws.rs.ext.Providers providers;        @GET      public Response get() {          ContextResolver<StorageEngine> resolver = providers.getContextResolver(StorageEngine.class, MediaType.WILDCARD_TYPE);          StorageEngine engine = resolver.get(StorageEngine.class);          ...      }  }  

However, I think the @javax.ws.rs.core.Context annotation and javax.ws.rs.ext.ContextResolver is really for types related to JAX-RS and supporting JAX-RS providers.

You may want to look for Java Context and Dependency Injection (JSR-299) implementations (which should be available in Java EE 6) or other dependency injection frameworks such as Google Guice to help you here.


Solution:2

Implement a InjectableProvider. Most likely by extending PerRequestTypeInjectableProvider or SingletonTypeInjectableProvider.

@Provider  public class StorageEngineResolver extends SingletonTypeInjectableProvider<Context, StorageEngine>{      public MyContextResolver() {          super(StorageEngine.class, new InMemoryStorageEngine());      }  }  

Would let you have:

@Context StorageEngine storage;  


Solution:3

I found another way. In my case i want to provide the user currently logged in as a User entity from my persitence layer. This is the class:

@RequestScoped  @Provider  public class CurrentUserProducer implements Serializable, ContextResolver<User> {        /**       * Default       */      private static final long serialVersionUID = 1L;          @Context      private SecurityContext secContext;        @Inject      private UserUtil userUtil;        /**       * Tries to find logged in user in user db (by name) and returns it. If not       * found a new user with role {@link UserRole#USER} is created.       *        * @return found user or a new user with role user       */      @Produces      @CurrentUser      public User getCurrentUser() {          if (secContext == null) {              throw new IllegalStateException("Can't inject security context - security context is null.");          }          return userUtil.getCreateUser(secContext.getUserPrincipal().getName(),                                        secContext.isUserInRole(UserRole.ADMIN.name()));      }        @Override      public User getContext(Class<?> type) {          if (type.equals(User.class)) {              return getCurrentUser();          }          return null;      }    }  

I only used implements ContextResolver<User> and @Provider to get this class discovered by Jax-Rs and get SecurityContext injected. To get the current user i use CDI with my Qualifier @CurrentUser. So on every place where i need the current user i type:

@Inject  @CurrentUser  private User user;  

And indeed

@Context  private User user;  

does not work (user is null).


Solution:4

A pattern that works for me: Add some fields on your Application subclass that provide the objects you need to inject. Then use an abstract base class to do the "injection":

public abstract class ServiceBase {        protected Database database;        @Context      public void setApplication(Application app) {          YourApplication application = (YourApplication) app;          database = application.getDatabase();      }  }  

All your services that need to access the database may now extend ServiceBase and have the database available automatically via the protected field (or a getter, if you prefer that).

This works for me with Undertow and Resteasy. In theory this should work across all JAX-RS implementations since injection of the Application is supported by the standard AFAICS, but I haven't tested it in other settings.

For me, the advantage over Bryant's solution was that I don't have to write some resolver class just so I can get at my application-scoped singletons like the database.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »