Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice don't inject to Jersey's resources

Parsed allover the whole internet, but can't figure out why this happens. I've got a simplest possible project (over jersey-quickstart-grizzly2 archetype) with one Jersey resource. I'm using Guice as DI because CDI doesn't want to work with Jersey either. The problem is that Guice can't resolve the class to use when injecting in Jersey's resources. It works great outside, but not with Jersey. Here is the Jersey resource:

import com.google.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("api")
public class MyResource {

    private Transport transport;

    @Inject
    public void setTransport(Transport transport) {
        this.transport = transport;
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getIt() {
        return transport.encode("Got it!");
    }
}

Transport interface:

public interface Transport {
    String encode(String input);
}

And it's realization:

public class TransportImpl implements Transport {
    @Override
    public String encode(String input) {
        return "before:".concat(input).concat(":after");
    }
}

Following Google's GettingStarted manual, I've inherited AbstractModule and bound my classes like this:

public class TransportModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Transport.class).to(TransportImpl.class);
    }
}

I get injector in main() with this, but don't really need it here:

Injector injector = Guice.createInjector(new TransportModule());

Btw, there's no problem when I try to do smth like this:

Transport transport = injector.getInstance(Transport.class);
like image 651
dazewell Avatar asked Dec 10 '22 18:12

dazewell


2 Answers

Jersey 2 already has a DI framework, HK2. You can either use it, or if you want, you can use the HK2/Guice bridge to bride your Guice module with HK2.

If you want to work with HK2, at the most basic level, it's not much different from the Guice module. For example, in your current code, you could do this

public class Binder extends AbstractBinder {
    @Override
    public void configurer() {
        bind(TransportImpl.class).to(Transport.class);
    }
}

Then just register the binder with Jersey

new ResourceConfig().register(new Binder());

One difference is the the binding declarations. With Guice, it "bind contract to implementation", while with HK2, it's "bind implementation to contract". You can see it's reversed from the Guice module.

If you want to bridge Guice and HK2, it's little more complicated. You need to understand a little more about HK2. Here's an example of how you can get it to work

@Priority(1)
public class GuiceFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
        GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
        Injector injector = Guice.createInjector(new TransportModule());
        GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(injector);
        return true;
    }
} 

Then register the feature

new ResourceConfig().register(new GuiceFeature());

Personally, I would recommend getting familiar with HK2, if you're going to use Jersey.

See Also:

  • HK2 Documentation
  • Custom Injection and Lifecycle Management

UPDATE

Sorry, I forgot to add that to use the Guice Bridge, you need to dependency.

<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>guice-bridge</artifactId>
    <version>2.4.0-b31</version>
</dependency>

Note that this is the dependency that goes with Jersey 2.22.1. If you are using a different version of HK2, you should make sure to use the same HK2 version that your Jersey version is using.

like image 59
Paul Samsotha Avatar answered Dec 28 '22 04:12

Paul Samsotha


Yes, in my opinion the answer above is correct. A very good way is to bridge Guice to HK2. I am not 100% sure if it neccessary to create a new TransportModule in the code above. Indeed Guice registers its injector in the ServletContext with the class name of Injector anyway, so it can be fetched from there:

        register(new ContainerLifecycleListener() {
        public void onStartup(Container container) {
            ServletContainer servletContainer = (ServletContainer)container;
            ServiceLocator serviceLocator = container.getApplicationHandler().getServiceLocator();
            GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
            GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
            Injector injector = (Injector) servletContainer.getServletContext().getAttribute(Injector.class.getName());
            guiceBridge.bridgeGuiceInjector(injector);
        }
        public void onReload(Container container) {
        }
        public void onShutdown(Container container) {
        }       
    });

Beside that it is not trivial how to configure the REST Endpoints in that way, so I wrote detailled blog about that here.

like image 27
Christian Aberger Avatar answered Dec 28 '22 06:12

Christian Aberger