Search code examples
javadependency-injectionjerseyhk2

ContainerRequestFilter with per-lookup-injectable not injected per request


I use Jersey 2 on a standalone Grizzly webserver. HK2 is used for CDI. I'd like to get a service with @PerLookup-scope injected into a jersey ContainerRequestFilter.

The service:

import org.glassfish.hk2.api.PerLookup;
import org.jvnet.hk2.annotations.Service;

@Service
@PerLookup
public class SessionManager {

    [...]
}

The filter:

import javax.annotation.Priority;
import javax.inject.Inject;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;

@Provider
@Priority(Priorities.AUTHENTICATION)
@PreMatching
public class HttpSessionFilter implements ContainerRequestFilter {
    @Inject
    private javax.inject.Provider<org.glassfish.grizzly.http.server.Request> requestProvider;

    @Inject
    private SessionManager sessionManager;

    [...]

}

My problem is this:

  • the filter is instantiated once (at startup)
  • the service is injected once (at filter-startup)
  • everything downstream from there is happening effectively at singleton scope

Question: how can I get the service injected per-request?

Update

The suggested approach using @Inject javax.inject.Provider<SessionManager> sessionManagerProvider makes logical sense to me, but sessionManagerProvider.get() returns null.

The hk2 serviceLocator is populated through the inhabitant-generator. It reports:

SystemDescriptor(
    implementation=com.skalio.skaliopush.http.SessionManager
    contracts={com.skalio.skaliopush.http.SessionManager}
    scope=org.glassfish.hk2.api.PerLookup
    qualifiers={}
    descriptorType=CLASS
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=null
    proxiable=null
    proxyForSameScope=null
    analysisName=null
    id=31
    locatorId=0
    identityHashCode=494317290
    reified=false)

And if I add the explicit binding, then it is also found like this:

SystemDescriptor(
    implementation=com.skalio.skaliopush.http.SessionManager
    contracts={com.skalio.skaliopush.http.SessionManager}
    scope=org.glassfish.jersey.process.internal.RequestScoped
    qualifiers={}
    descriptorType=CLASS
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@78b729e6
    proxiable=null
    proxyForSameScope=null
    analysisName=null
    id=39
    locatorId=0
    identityHashCode=2041416495
    reified=false)

2nd Update

I am using two service locators: One created explicitly, and populated through the HK2 inhabitant generator; and the one that Jersey creates. They are joined through a BridgingInjectionResolver.

@peeskillet's response works, when I add the explicit binding to Jersey's service locator. Adding the binding to the other service locator results in the above (sessionManagerProvider.get() returns null).


Solution

  • It should work if you also use javax.inject.Provider<SessionMananger>. The default scope is per lookup. But I guess you need to load it lazily.

    Not sure how it differs functionally from RequestScoped in Jersey. I thought that in a PerLookup scope, it would get created twice if I inject it once into the filter and once into the resource class, but just testing right now, even in PerLookup scope, it was still only created once for each request.

    Alternatively, just to be sure, you may just want to bind it in a RequestScoped if that is what you really want.

    register(new AbstractBinder(){
        protected void configure() {
            bind(SessionManager.class).to(SessionManager.class)
                                      .in(RequestScoped.class);
        }
    });