Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject an object into jersey request context?

I have this scenario where I want to write a filter and I want this filter to insert some object into the current request and pass it on so that when the resource class gets the request it can use the object.

Filter class

@Override
public void filter(ContainerRequestContext request) throws IOException {
    MyObject obj = new MyObject();
    // Inject MyObject to request which I dont know how
}

Resource Class

@PUT @Consumes("application/json")
@Path("/")
public String create(
        JSONParam sample,
        @Context MyObject obj) {

    System.out.println(obj.getName());

    return "";
}
like image 707
armin Avatar asked Dec 27 '14 08:12

armin


2 Answers

You could just use ContainterRequestContext.setProperty(String, Object). Then just inject the ContainerRequestContext

@Override
public void filter(ContainerRequestContext crc) throws IOException {
    MyObject obj = new MyObject();
    crc.setProperty("myObject", myObject);
}

@POST
public Response getResponse(@Context ContainerRequestContext crc) {
    return Response.ok(crc.getProperty("myObject")).build();
}

Another option to inject the MyObject directly is to use the HK2 functionality Jersey 2 offers.

Create a factory the inject the ContainerRequestContext and return the MyObject. For example

import javax.inject.Inject;
import javax.ws.rs.container.ContainerRequestContext;
import jetty.plugin.test.domain.MyObject;
import org.glassfish.hk2.api.Factory;

public class MyObjectFactory implements Factory<MyObject> {
    
    private final ContainerRequestContext context;
    
    @Inject
    public MyObjectFactory(ContainerRequestContext context) {
        this.context = context;
    }

    @Override
    public MyObject provide() {
        return (MyObject)context.getProperty("myObject");
    }

    @Override
    public void dispose(MyObject t) {}  
}

You then need to bind the factory:

public class InjectApplication extends ResourceConfig {
    
    public InjectApplication() {
        ...
        register(new AbstractBinder(){
            @Override
            protected void configure() {
                bindFactory(MyObjectFactory.class)
                        .to(MyObject.class)
                        .in(RequestScoped.class);
            } 
        });
    }
}

With the same setting of the property as in the filter example above, you can then just inject the MyObject with the @Context

@GET
public Response getTest(@Context MyObject myObject) {
    return Response.ok(myObject.getMessage()).build();
}

  • See more at Custom Injection

UPDATE

Please see this question for a problem with this implementation.

See Also:

  • If you're using web.xml instead of a ResourceConfig
like image 105
Paul Samsotha Avatar answered Nov 13 '22 09:11

Paul Samsotha


I've got a solution to this that doesn't require a DI container, but still gives most of the benefit.

There's two parts. The first is how to get instances into the @Context injection mechanism instead of providing classes in the ApplicationConfig object.

Here's a technique for doing that:

private static class CustomContextResteasyBootstrap extends org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap{
    private final Map<Class<?>, Object> additionalContextObjects = new HashMap<Class<?>, Object>();

    public <E> CustomContextResteasyBootstrap addContextObject(Class<? super E> clazz, E obj){
        additionalContextObjects.put(clazz, obj);
        return this;
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        super.contextInitialized(event);
        deployment.getDispatcher().getDefaultContextObjects().putAll(additionalContextObjects);
    }

}

and you use it like this:

        webAppContext.addEventListener(
                new CustomContextResteasyBootstrap()
                    .addContextObject(MyCustom.class, myCustom)
                    .addContextObject(AnotherCustom.class, anotherCustom)
                    // additional objects you wish to inject into the REST context here
            );

now you can use those classes with the @Context annotation:

@GET
public MyCustom echoService(@Context MyCustom custom) {
    return custom;
}

The next part of the puzzle is how to provide per-request context objects. To do this, add the following code somewhere near the top of the jax-rs call hierarchy (basically, anything that gets called below this line will get access to the context object):

    ResteasyProviderFactory.pushContext(MyContextSpecific.class, new MyContextSpecific());

You can then reference this via injection anywhere below that level:

@GET
public String contextSpecificEchoService(@Context MyContextSpecific contextSpecific) {
    return custom.toString();
}

This is poor-man's DI, but it works really well for embedded rest servers.

like image 1
Kevin Day Avatar answered Nov 13 '22 11:11

Kevin Day