1

I already looked at a lot of posts and nothing seems to work quite as i liked it to.

I want to inject an object into ContainerRequestContext properties from a filter and retrieve it later in other classes.

here is my filter:

@Priority(Priorities.AUTHENTICATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext containerRequestContext) throws IOException {

        containerRequestContext.setProperty("myObject", new Object());
    }
}

here is the class I want access to ContainerRequestContext:

@Provider
public class SessionContextProvider implements ISessionContextProvider {
    @Context
    private ContainerRequestContext request;

    @Override
    public Object getSessionContext() {
        return request.getProperty("mySessionContext");
    }
}

and my spring config:

@Bean(name="sessionContextProvider")
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public ISessionContextProvider sessionContextProvider() {
    return new SessionContextProvider();
}

Everything works as expected if I inject ContainerRequestContext into my web resource. However if call my provider class ContainerRequestContext is always null.

I don't seem why this would not work.

Reagrds Jonas

jonas.hartwig
  • 857
  • 3
  • 8
  • 19
  • I made sure that the filter and the SessionContextProvider instance both run on the same thread using: Thread.currentThread().getId() – jonas.hartwig Mar 17 '16 at 14:15

2 Answers2

1

The problem is that with the Jersey/Spring integration, it allows us to successfully inject Spring beans into Jersey components, but this is not always true the other way around.

Jersey has it's own DI framework, HK21, and it is responsible for handle the injections with Jersey components. With the Jersey Spring integration, Jersey will lookup the Spring Bean, and take it as is, it won't inject it with any dependencies, I guess assuming Spring should take care of it's own injections.

That being said, if you don't require the ISessionContextProvider to be a Spring bean, then you can just make it an HK2 service. It's pretty simple. If you don't require any special initialization, you can just let HK2 create it. Here a simple configuration

public JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(new AbstractBinder() {
            bind(SessionContextProvider.class)
                    .to(ISessionContextProvider.class)
                    .in(RequestScoped.class);
        });
    }
}

And that's it. You have an injectable ISessionContextProvider2.

If you require the ISessionContextProvider provider to be a Spring bean, then another option is to grab the bean from the Spring ApplicatinContext, and explicitly inject it yourself, using HK2's analogue of the ApplicationContext, its ServiceLocator. To do that we would need to use a Factory to do all the work transparently, so you can still inject the bean without doing any extra work on the outside

import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.springframework.context.ApplicationContext;

public class SessionContextProviderFactory 
        implements Factory<SessionContextProvider> {

    private final ISessionContextProvider provider;

    @Inject
    public SessionContextProviderFactory(ApplicationContext ctx,
                                         ServiceLocator locator) {
        provider = ctx.getBean(ISessionContextProvider.class);
        locator.inject(provider);
    }

    @Override
    public ISessionContextProvider provide() {
        return provider;
    }

    @Override
    public void dispost(ISessionContextProvider provider) { /* noop */ }
}

Then just register the factory

public JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(new AbstractBinder() {
            bindFactory(SessionContextProviderFactory.class)
                    .to(ISessionContextProvider.class)
                    .in(RequestScoped.class);
        });
    }
}

1 -
2 - See also Dependency injection with Jersey 2.0

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for the explanation. That makes totally sense.however if i add the code the factory is not called and the request object i want to ger is still null. If i remove the bean definition spring explodes. Any suggestions? I possibly should have mentioned i used approach 2. – jonas.hartwig Mar 17 '16 at 15:32
  • How were configuring Jersey before? Was it in a ResourceConfig or web.xml? Also where exactly are you trying to inject session provider? Is it inside a Jersey component or in another Spring component? The factory will only be called if you are trying to inject the session provider into a Jersey component – Paul Samsotha Mar 17 '16 at 15:37
  • It is from within another spring injected component :/ – jonas.hartwig Mar 17 '16 at 16:20
  • And where is that component being injected? Whatever the top parent component is, is it being injected into a Jersey component? Otherwise how do you expect it to get a Jersey request context if it never goes through the Jersey request cycle. If the top parent component is being injected into a Jersey component, then I'm sure it's possible. You may need to traverse the object tree to get the session provide – Paul Samsotha Mar 17 '16 at 16:28
  • Yes, the top level component is always a jersey one. However in between the jersey component and the spring bean having ContainerRequestContext might be multiple other spring instances. – jonas.hartwig Mar 18 '16 at 07:20
  • I will play around with this a little bit later and see what I can come up with. I will let you know if I can find a workable solution – Paul Samsotha Mar 18 '16 at 07:25
  • Thanks for the help! We currently have multiple Spring injection based Jersey RESTful services. Now we would like to increase security and change to token based authentication. So my idea behind this is that the filter grabs the header, verifies it and injects a kind of WhoAmI object into the request scope (using the current HttpMessage => ContainerRequestContext) In my spring beans then I would like to access this WhoAmI (SessionContext) context via a Spring injectable SessionContextProvider (request scoped) which should grab the information from the current Http Message. – jonas.hartwig Mar 18 '16 at 08:32
1

I found a workaround. I could inject the Spring HttpServletRequest into my AuthorizationFilter and set the SessionContext on this one.

@Autowired
private HttpServletRequest request;

....

request.setAttribute("mySessionContext", sessionContext);

And then because HttpServletRequest is known to spring and besically represents the same thing in my SessionContextProvider I do the same:

@Autowired
private HttpServletRequest request;

@Override
public SessionContext getSessionContext() {
    return (SessionContext) request.getAttribute("mySessionContext");
}

I dont think this is the best solution possible. But it works. Ill wait for peeskillet for any other input if there is a better solution.

Regards Jonas

jonas.hartwig
  • 857
  • 3
  • 8
  • 19
  • The only other thing I could find is to use [`RequestContextHolder`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/request/RequestContextHolder.html). If you register Jersey's [`RequestContextFilter`](https://github.com/jersey/jersey/blob/master/ext/spring4/src/main/java/org/glassfish/jersey/server/spring/scope/RequestContextFilter.java), it binds the `ContainerRequestContext` properties to `RequestAttributes`. So you could do `RequestContextHolder.getRequestAttributes().getAttribute(..)`, and it delegates to `ContainerRequestContext.getProperty(..)` – Paul Samsotha Mar 18 '16 at 10:28
  • It's pretty much the same idea as setting a property on the HttpServletRequest. I don't see much difference – Paul Samsotha Mar 18 '16 at 10:30