tutorial jax example java jersey jax-rs

jax - web service rest java eclipse



Uso de @Context, @Provider y ContextResolver en JAX-RS (4)

Me estoy familiarizando con la implementación de servicios web REST en Java utilizando JAX-RS y me encontré con el siguiente problema. Una de mis clases de recursos requiere acceso a un backend de almacenamiento, que se StorageEngine detrás de una interfaz StorageEngine . Me gustaría inyectar la instancia actual de StorageEngine en la clase de recurso que sirve las solicitudes REST y pensé que una buena forma de hacerlo sería mediante el uso de la anotación @Context y una clase apropiada de ContextResolver . Esto es lo que tengo hasta ahora:

En MyResource.java :

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

En 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; } }

Estoy usando com.sun.jersey.api.core.PackagesResourceConfig para descubrir los proveedores y las clases de recursos automáticamente, y de acuerdo con los registros, recoge la clase StorageEngineProvider muy bien (marcas de tiempo y cosas innecesarias que se StorageEngineProvider intencionalmente):

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

Sin embargo, el valor del storage en mi clase de recurso siempre es null ; StorageEngineProvider ni Jersey, llama al constructor de StorageEngineProvider ni a su método getContext . ¿Qué estoy haciendo mal aquí?


Encontré otra manera. En mi caso, quiero proporcionar al usuario que ha iniciado sesión actualmente como una entidad de Usuario desde mi capa de persitencia. Esta es la clase:

@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; } }

Solo usé los implements ContextResolver<User> y @Provider para obtener esta clase descubierta por Jax-Rs e inyectar SecurityContext . Para obtener el usuario actual, uso CDI con mi Calificador @CurrentUser . Así que en cada lugar donde necesito el usuario actual escribo:

@Inject @CurrentUser private User user;

Y de hecho

@Context private User user;

no funciona (el usuario es nulo).


Implementar un InjectableProvider . Lo más probable es que se extienda PerRequestTypeInjectableProvider o SingletonTypeInjectableProvider.

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

Te dejaría tener:

@Context StorageEngine storage;


No creo que haya una forma específica de JAX-RS para hacer lo que quieres. Lo más cercano sería hacer:

@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); ... } }

Sin embargo, creo que la anotación @ javax.ws.rs.core.Context y javax.ws.rs.ext.ContextResolver son realmente para tipos relacionados con JAX-RS y proveedores de JAX-RS compatibles.

Puede buscar implementaciones de Contexto de Java y de Inyección de Dependencias (JSR-299) (que deberían estar disponibles en Java EE 6) u otros marcos de inyección de dependencias como Google Guice para que lo ayuden aquí.


Un patrón que me funciona: agregue algunos campos en su subclase de aplicación que proporcionen los objetos que necesita inyectar. Luego use una clase base abstracta para hacer la "inyección":

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

Todos los servicios que necesitan acceder a la base de datos ahora pueden extender ServiceBase y tener la base de datos disponible automáticamente a través del campo protegido (o un receptor, si así lo prefiere).

Esto me funciona con Undertow y Resteasy. En teoría, esto debería funcionar en todas las implementaciones de JAX-RS ya que la inyección de la aplicación es compatible con el estándar AFAICS, pero no la he probado en otras configuraciones.

Para mí, la ventaja sobre la solución de Bryant era que no tengo que escribir algunas clases de resolución solo para poder acceder a mis singletons de ámbito de aplicación como la base de datos.