2

Using Jersey 2.25 with @NamedBinding sub-resource methods aren't getting filters called.

In the following, the getData() method triggers the AuthorizationFilter, but the getSubDetails() method does not.

@NameBinding
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface Secured {
    Permission[] value() default {};
}


@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {
...
}

@Produces(MediaType.APPLICATION_JSON)
@Path("api/v1/jobs/{jobId}/document-sources")
@Secured(Permission.JOB_READER)
public class MyEndpoint {


    @Path("/{subId}")
    @GET
    @Secured(Permission.READ)
    public Class<?> getData(@PathParam("subId") String subId){
        return "data";
    }

    @Path("/{subId}/details")
    @Secured(Permission.READ)
    public Class<?> getSubDetails(@PathParam("subId") String subId){
        return SubDetails.class;
    }
}

The JAX-RS documentation ( https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ContainerRequestFilter.html ) seems to imply that the filter should be working on sub-resources.

Am I doing something wrong? Do NameBinding filters not apply to sub-resource methods anymore?

Kevin Day
  • 16,067
  • 8
  • 44
  • 68
  • (In regards to your first link) - For me the tests still pass. For some reason though I couldn't use a 1000 status code. It was freezing on me. Had to change to 500 status code in the filter. But all the tests still pass. I used 2.25.1. The difference I can see is that in my tests, the annotation was on the sub-resource class, you have it on the locator method. – Paul Samsotha Apr 26 '18 at 01:51
  • Thanks for pointing that out - the issue with the unit test was that the response code was 1000. This still leaves the problem that I need the authorization filter to run before the subclass is returned (there is setup work done in the authorization filter that is required to determine which sub-resource I return). It really looks to me like there isn't a way to filter on the sub-resource method itself - only the sub-resource class itself... That's a bummer. – Kevin Day Apr 26 '18 at 01:56
  • Why don't you make that determination in the locator method? – Paul Samsotha Apr 26 '18 at 01:58
  • The determination requires expensive database calls. If we make the determination in the locator method, we wind up having to repeat the database calls in the authorization filter. When we just use the authorization filter, we can cache the results of that call in the request context. We could do that in the locator method as well, but now I've got my authorization logic duplicated. So I guess the real answer is "because it's ugly" :-) – Kevin Day Apr 26 '18 at 02:03
  • You could do the auth in the filter, and inject some object into the locator method that tells details about the result (to make the determination). See https://stackoverflow.com/a/27666797/2587435 – Paul Samsotha Apr 26 '18 at 02:08
  • 1
    Actually, I don't know if that will work either. I think the problem is not so much with name-binding, but that filters are not called prior to locator methods being called. Maybe a `@PreMatching` filter? – Paul Samsotha Apr 26 '18 at 02:14
  • Yep - that's what our auth filter does (we set a property, then use a Factory to provide that value, and pull it in with javax.inject.Provider in the resource class or @Context for a method parameter). But the auth filter doesn't run on the locator method (that's the problem). In the end, I'll probably just factor out the service that does the DB lookup so it can be used in the auth service and the locator method - just annoying to have to fight the framework like this. – Kevin Day Apr 26 '18 at 02:16

1 Answers1

1

For anyone else running into this:

Sub-resource locator methods do not have NameBinding filters run against them (at least in Jersey). I can't find any documentation that says whether this is correct or not.

In our case, the filter was important because it was adding objects to the request context which were used by the locator method to determine which sub-resource to return. The creation of those objects is somewhat expensive (involves database calls), so we didn't want to just duplicate the object creation code in the sub-resource locator method (plus that would violate DRY).

The solution I wound up with was moving the object creation logic into a Factory, and have the Factory use request context caching to ensure that it only does the object initialization a single time. The factory is then used in the filter (for authorization) and in the sub-resource method (for determining which sub-resource to send). Then each sub-resource has to have the authorization filter annotation.

Kevin Day
  • 16,067
  • 8
  • 44
  • 68