0

I'm really new to DI and am having tons of problems with basic things in JAX-RS.

I have a filter called SentryAuthFilter which subclasses an abstract class called AuthFilter. AuthFilter implements ContainerRequestFilter, ContainerResponseFilter and WriterInterceptor. Because AuthFilter is abstract, the methods, like configure(), matchResource(), ... are implemented actually in SentryAuthFilter() (and other classes that subclass AuthFilter.

In SentryFilter, I need access to the HTTPServletRequest and HTTPServletResponse in my filter method.

@Priority(Priorities.AUTHENTICATION)
public class SentryAuthFilter extends AuthFilter {

    @Context
    private HttpServletRequest request;

    @Context
    private HttpServletResponse response;

    ...



@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
    SentryHttpServletResponseWrapper responseWrapper = new SentryHttpServletResponseWrapper(response);
    try {
        tomcatSSOFilter.doFilter(request, responseWrapper, (request1, response1) -> {});
    } catch (Exception e) {
        LOGGER.error("Error occurred executing TomcatSSOFilter", e);
        requestContext.abortWith(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build());
        return;
    }

    ...
 }

The problem is that request and response is null. I've looked at 8 different questions regarding this topic and I can't find one that explains how HTTPServletRequest and HTTPServletResponse are injected. They just say that it is a singleton proxy object and you shouldn't need to explicitly inject it, just use @Context. Clearly, @Context is not working.

The class that actually calls SentryFilter or any of the other classes that implement AuthFilter is called DynamicAuthFilter. This DynamicAuthFilter is "registered" in a class that implements DynamicFeature

@Provider
public class AuthenticationDynamicFeature implements DynamicFeature {

    private final List<javax.inject.Provider<? extends AuthFilter>> filterProviders;

    /**
     * @param permissionAuthFilter permissionAuthFilter
     * @param aaaAuthFilter aaaAuthFilter
     * @param sentryAuthFilter sentryAuthFilter
     */
    @Inject
    public AuthenticationDynamicFeature(final javax.inject.Provider<SomeAuthFilter> someAuthFilter,
                                        final javax.inject.Provider<AnotherAuthFilter> anotherAuthFilter,
                                        final javax.inject.Provider<SentryAuthFilter> sentryAuthFilter) {
        this.filterProviders = ImmutableList.of(someAuthFilter, anotherAuthFilter, sentryAuthFilter);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {

            List<AuthFilter> filters = filterProviders.stream()
                    .map(javax.inject.Provider::get)
                    .filter(f -> f.matchResource(resourceInfo))
                    .peek(AuthFilter::configure)
                    .collect(Collectors.collectingAndThen(Collectors.toList(), ImmutableList::copyOf));

        context.register(new DynamicAuthFilter(filters));
    }
}

According to this, it seems like context registration is how @Context values are filled. So I'm not sure if the issue is that registration in AuthenticationDynamicFeature is wrong, or if I'm missing something else. DynamicAuthFilter does nothing else except call SentryFilter, AnotherAuthFilter, etc, so I don't use @Context in it.

Sorry for the long post, but I'm really confused.

Jeremy Fisher
  • 2,510
  • 7
  • 30
  • 59
  • Maybe [just manually inject it](https://stackoverflow.com/a/35953104/2587435) – Paul Samsotha Jan 09 '18 at 22:47
  • I saw that post. How do I get access to the HTTPServletRequest and Response object? In that post the guy explicitly passes in `authenticator`, but where would I get the `request` or `response`? – Jeremy Fisher Jan 09 '18 at 22:57
  • You inject the filter. You have the filter. locator.inject(filter) – Paul Samsotha Jan 09 '18 at 23:02
  • So use `ServiceLocator` to inject it, then register it? How will that fill in the request and response values? The filter itself needs those values, so how would injecting the filter make @Context inject HTTPServletRequest and HTTPServletResponse? – Jeremy Fisher Jan 09 '18 at 23:06
  • You don't need to register it. Just inject it. The locator will inject a proxy. Read the rest of the post – Paul Samsotha Jan 09 '18 at 23:07
  • I did read it, but I'm just confused with what specifically to inject. That whole `List filters...` line is important because it collects the different filters that are specified for a particular API based on annotations. `DynamicAuthFilter` is needed to actually call those filters. So are you saying my `configure` method should only be the `List filters` line, then create a DynamicAuthFilter object passing in filters, and then using `ServiceLocator` to inject `DynamicAuthFilter`? – Jeremy Fisher Jan 09 '18 at 23:16
  • Use the locator to inject any object you want injected. Is the the DynamicAuthFilter you want injected? No. You are already passing all the filters to it. What you want to inject is the filter that is not getting the request and response, the SentryAuthFilter – Paul Samsotha Jan 09 '18 at 23:18
  • Alright, is there a way other than ServiceLocator to inject SentryFilter? Apparently getting ServiceLocator in jersey isn't trivial. – Jeremy Fisher Jan 09 '18 at 23:31
  • You should just be able to inject it into your feature. It's a service like any other service. – Paul Samsotha Jan 09 '18 at 23:34
  • Do you have an example of this? When I tried it I got errors? – Jeremy Fisher Jan 10 '18 at 00:13
  • https://gist.github.com/psamsotha/6ac7816b4bf17eaebb87a876e7c730c7 – Paul Samsotha Jan 10 '18 at 00:40
  • I think I need to bind the ServiceLocator, otherwise i get "No implementation" errors, which comes back to my first point of how to "get" or make a ServiceLocator – Jeremy Fisher Jan 10 '18 at 01:20
  • What version of Jersey are you using? – Paul Samsotha Jan 10 '18 at 01:24
  • Jersey 2.19 it seems – Jeremy Fisher Jan 10 '18 at 01:40
  • Should work. Did you run my test? – Paul Samsotha Jan 10 '18 at 01:41
  • I implemented your changes in my `AuthenticationDynamicFeature`. I added ServiceLocator to my Constructor that already has an @Inject and used `locator.inject(sentryAuthFilter) there. There's not really another place where I can inject it. I can't inject it in configure because I can't easily make a `SentryAuthFilter` (it has arguments passed to it's constructor) which I don't think should me instantiated in AuthenticationDynamicFeature. – Jeremy Fisher Jan 10 '18 at 01:43
  • How can you do that when it is a Provider and not the actual filter? Just do it in the map method on your stream – Paul Samsotha Jan 10 '18 at 01:45
  • so instead of `.map(javax.inject.Provider::get)` do `.map(this.locator.inject(javax.inject.Provider))`? That fails with Object is not a functional interface – Jeremy Fisher Jan 10 '18 at 02:19
  • 1) get the filter from the provider, 2) inject the filter, 3) return the filter. – Paul Samsotha Jan 10 '18 at 02:32

0 Answers0