How to get resource method matched to URI before Jersey invokes it?

java, jersey, jersey-1.0

Solution

I figured out how to solve my problem using only Jersey. There's apparently no way to match a request's URI to the method that will be matched before that method is invoked, at least in Jersey 1.x. However, I was able to use a `ResourceFilterFactory` to create a `ResourceFilter` for each individual resource method - that way these filters can know about the destination method ahead of time.

Here's my solution, including the validation for required query params (uses Guava and JSR 305):

public final class ValidationFilterFactory implements ResourceFilterFactory {

    @Override
    public List<ResourceFilter> create(AbstractMethod abstractMethod) {

        //keep track of required query param names
        final ImmutableSet.Builder<String> requiredQueryParamsBuilder =
                ImmutableSet.builder();

        //get the list of params from the resource method
        final ImmutableList<Parameter> params =
                Invokable.from(abstractMethod.getMethod()).getParameters();

        for (Parameter param : params) {
            //if the param isn't marked as @Nullable,
            if (!param.isAnnotationPresent(Nullable.class)) {
                //try getting the @QueryParam value
                @Nullable final QueryParam queryParam =
                        param.getAnnotation(QueryParam.class);
                //if it's present, add its value to the set
                if (queryParam != null) {
                    requiredQueryParamsBuilder.add(queryParam.value());
                }
            }
        }

        //return the new validation filter for this resource method
        return Collections.<ResourceFilter>singletonList(
                new ValidationFilter(requiredQueryParamsBuilder.build())
        );
    }

    private static final class ValidationFilter implements ResourceFilter {

        final ImmutableSet<String> requiredQueryParams;

        private ValidationFilter(ImmutableSet<String> requiredQueryParams) {
            this.requiredQueryParams = requiredQueryParams;
        }

        @Override
        public ContainerRequestFilter getRequestFilter() {
            return new ContainerRequestFilter() {
                @Override
                public ContainerRequest filter(ContainerRequest request) {

                    final Collection<String> missingRequiredParams =
                            Sets.difference(
                                    requiredQueryParams,
                                    request.getQueryParameters().keySet()
                            );

                    if (!missingRequiredParams.isEmpty()) {

                        final String message =
                                "Required query params missing: " +
                                Joiner.on(", ").join(missingRequiredParams);

                        final Response response = Response
                                .status(Status.BAD_REQUEST)
                                .entity(message)
                                .build();

                        throw new WebApplicationException(response);
                    }

                    return request;
                }
            };
        }

        @Override
        public ContainerResponseFilter getResponseFilter() {
            return null;
        }
    }
}

And the `ResourceFilterFactory` is registered with Jersey as an init param of the servlet in `web.xml`:

<init-param>
    <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
    <param-value>my.package.name.ValidationFilterFactory</param-value>
</init-param>

At startup, `ValidationFilterFactory.create` gets called for each resource method detected by Jersey.

Credit goes to this post for getting me on the right track: How can I get resource annotations in a Jersey ContainerResponseFilter

Problem

I'm trying to implement a `ContainerRequestFilter` that does custom validation of a request's parameters. I need to look up the resource method that will be matched to the URI so that I can scrape custom annotations from the method's parameters. Based on this answer I should be able to inject `ExtendedUriInfo` and then use it to match the method: ``` public final class MyRequestFilter implements ContainerRequestFilter { @Context private ExtendedUriInfo uriInfo; @Override public ContainerRequest filter(ContainerRequest containerRequest) { System.out.println(uriInfo.getMatchedMethod()); return containerRequest; } } ``` But `getMatchedMethod` apparently returns `null`, all the way up until the method is actually invoked (at which point it's too late for me to do validation). How can I retrieve the `Method` that will be matched to a given URI, before the resource method is invoked? For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351.

Original source

Related problems