I have a ResourceFilterFactory
that returns a ContainerRequestFilter
if an annotation is present on the AbstractMethod
.
The filter reads a cookie or API key and checks that it is still valid, and sets a property on the request. The check is expensive, so I only want it to occur when the annotation is present.
I want to inject the resulting object into my resource, but it seems that injection happens before ResourceFilterFactory
filters are applied. If I add the filter to the ResourceConfig
as a PROPERTY_CONTAINER_REQUEST_FILTERS
, then it happens before the injection, but it does not have access to the AbstractMethod
.
How do I bridge the gap, so that the filter is applied and the field is injected only if the method has the annotation?
My code looks something like this. AuthFilter
is called after UserIdProvider
, so c.getProperties().get(USER_ID_PROPERTY)
is always null
.
public class AuthFilter implements ContainerRequestFilter {
public ContainerRequest filter(ContainerRequest request) {
Integer userId = …; // expensive lookup
request.getProperties().set(USER_ID_PROPERTY, userId);
return request;
}
}
public class AuthFilterFactory implements ResourceFilterFactory {
// …
public List<ResourceFilter> create(AbstractMethod am) {
if (am.isAnnotationPresent(RequireAuth.class) || am.getResource().isAnnotationPresent(RequireAuth.class)) {
return Collections.singletonList(new ResourceFilter() {
public ContainerRequestFilter getRequestFilter() {
return authFilter;
}
});
}
else {
return null
}
}
}
InjectableProvider:
@Provider
public class UserIdProvider implements InjectableProvider<UserId, Parameter> {
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
public Injectable<? extends Object> getInjectable(ComponentContext ic, UserId a, final Parameter t) {
return new AbstractHttpContextInjectable<Integer>() {
public Integer getValue(HttpContext c) {
return (Integer) c.getProperties().get(USER_ID_PROPERTY);
}
}
}
}
Resource:
@Path("/user")
public class UserResource {
@UserId
Integer userId;
// userId should only be set on this method
@GET
@RequireAuth
public UserDetails get() {
// …
}
// the auth filter should not be called for this method
@POST
@Path("/login")
public String login() {
// …
}
}