javaspringspring-bootjerseyspring-jersey

Jersey factory tries to create @Context variable twice


I have a problem with Jersey 2.26 and Spring Boot 2.

I added a factory to inject @Context variable into methods, but it does it twice, first before the method and then again after the method:

@GET
@Path("/user-entitlements")
public Set<String> getUserEntitlements(@Context User user) {
    return service.getUserEntitlements(user);
}

I have a factory that creates those users:

public class UserFactory implements Factory<User> {

    private final User user;

    @Inject
    public UserFactory(HttpServletRequest request, RequestIdentityService requestIdentityService) {
        String userName = requestIdentityService.getCurrentUser(request);
        user = new User(userName);
    }

    @Override
    public User provide() {
        return user;
    }

    @Override
    public void dispose(User instance) {
    }
}

And in jersey configuration I register this factory:

    register(
        new AbstractBinder() {
            @Override
            public void configure() {
                bindFactory(UserFactory.class)
                    .to(User.class)
                    .in(RequestScoped.class);
            }
        }
    );

It worked fine previously, but after I upgraded the application to jersey 2.26 and spring boot 2, it stopped working and throws exception:

java.lang.IllegalStateException: Proxiable context org.glassfish.jersey.inject.hk2.RequestContext@3a94c454 findOrCreate returned a null for descriptor SystemDescriptor(
    implementation=org.glassfish.jersey.inject.hk2.SupplierFactoryBridge
    contracts={javax.servlet.http.HttpServletRequest}
    scope=org.glassfish.jersey.process.internal.RequestScoped
    qualifiers={}
    descriptorType=PROVIDE_METHOD
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@464366a8
    proxiable=true
    proxyForSameScope=false
    analysisName=null
    id=20
    locatorId=0
    identityHashCode=1360647282
    reified=true) and handle ServiceHandle(SystemDescriptor(
    implementation=com.rest.UserFactory
    contracts={org.glassfish.hk2.api.Factory}
    scope=org.glassfish.hk2.api.PerLookup
    qualifiers={}
    descriptorType=CLASS
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@33335025
    proxiable=null
    proxyForSameScope=null
    analysisName=null
    id=172
    locatorId=0
    identityHashCode=790201459
    reified=true),1915928862)

The exception shows up because UserFactory is called twice. First it is called before the getUserEntitlements is called, to inject the User. But after the method returns, it is called again (and this time it gets a HttpRequest implementation that doesn't allow some operations).

Why it could be called twice? As if I would asked it to enchance both the request and response.


Solution

  • The problem is that in new Jersey they moved away from hk2 into an abstraction layer above it, and made the code Java 8 aware (because it is an implementation of Java EE 8 - https://jersey.github.io/release-notes/2.26.html).

    So to fix it I had to:

    1. Import the correct AbstractBinder - org.glassfish.jersey.internal.inject.AbstractBinder(NOT the previous one from HK2 - org.glassfish.hk2.utilities.binding.AbstractBinder).

    2. Update the factory to implement Supplier<User> instead of Factory<User>.

    After that changes everything works as previously, factory is called only once.