4

Thanks to migration to jersey 2 I need to migrate from guice to HK2. I have an Assisted injection approach for some of my dependencies which I couldn't get my head around to implement in HK2. It looks like it's supposed to be solved via Custom Injection Resolvers but I don't really see how. The examples are not clear enough for me..

Here is how it looks on Guice:

public interface MyFactory {
    public MyClass createMyClass(@Assisted String dynamicParameter);
    public HisClass createHisClass(@Assisted String dynamicParameter);
    ...
}

binder.install(new FactoryModuleBuilder().build(MyFactory.class));

public class MyClass {
   ...
   @Inject
   public MyClass(@Assisted String dynamicParameter, SomeService someOtherServiceInjectedAutomatically){
      ...
   }
}

How can I implement this on HK2?

Natan
  • 2,816
  • 20
  • 37

2 Answers2

5

After posting the question I thought of doing this:

public class MyFactoryImpl implements MyFactory{

   private final SomeService someService;

   @Inject
   public MyFactoryImpl(SomeService someService){
      this.someService = someService;
   }

   public MyClass createMyClass(String dynamicParameter){
      return new MyClass(dynamicParameter, someService);
   }

   ...
}
Natan
  • 2,816
  • 20
  • 37
2

There is a Guice-Bridge :-D

<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>guice-bridge</artifactId>
    <version>${hk2.version}</version>
</dependency>

Here is an example using Guice 3.0 and HK2 2.3.0 (which comes bundled with Jersey 2.13). This is just a standalone, but it should work in Jersey environment just the same.

Guice classes

public class GuiceGreeter {
    public String getGreeting(String name) {
        return "Hello, " + name;
    }
}

import com.google.inject.assistedinject.Assisted;
import javax.inject.Inject;

public class Message {
    private final String message;  
    @Inject
    public Message(GuiceGreeter greeter, @Assisted String name) {
        message = greeter.getGreeting(name);
    }
    public String getMessage() {
        return message;
    }
}

public interface GuiceMessageFactory {
    public Message getMessage(String name);
}

import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;

public class GuiceMessageModule extends AbstractModule {
    @Override
    protected void configure() {
        install(new FactoryModuleBuilder().build(GuiceMessageFactory.class));
        bind(GuiceGreeter.class);
    }   
}

HK2 service, which injects the Guice factory

import javax.inject.Inject;

public class HK2Service {
    private final GuiceMessageFactory messageFactory;

    @Inject
    public HK2Service(GuiceMessageFactory messageFactory) {
        this.messageFactory = messageFactory;
    }

    public void printMessage(String name) {
        System.out.println(messageFactory.getMessage(name).getMessage());
    }
}

Main

import com.google.inject.Guice;
import com.google.inject.Injector;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.ServiceLocatorFactory;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;

public class Main {
    public static void main(String[] args) {
        // Create service locator. In Jersey context, you should be able to
        // inject the `ServiceLocator` into the `Application/ResourceConfig` 
        // subclass constructor, or as a field
        ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
        ServiceLocator locator = factory.create("SimpleServiceLocator");

        // bridge the two frameworks to allow Guice injected services
        GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
        Injector injector = Guice.createInjector(new GuiceMessageModule());
        GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
        guiceBridge.bridgeGuiceInjector(injector);

        // Add my HK2 Service
        ServiceLocatorUtilities.addClasses(locator, HK2Service.class);

        // Look up HK2 service. If this lookup works, `@Inject` in Jersey should.
        HK2Service service = locator.getService(HK2Service.class);
        service.printMessage("peeskillet");
    }
}

This prints out "Hello, peeskillet". See comment below main method to obtain ServiceLocator in Jersey app. And in case you are unfamailiar with the ServiceLocator, all the bindings you add with an AbstractBinder will get put in the service locator context also, so you don't have to explicitly add the class as I am going above with HK2Service.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Although this is a useful answer to me in my context in case I decide to keep Guice as my DI, it's not an answer to the question asked. So I can't mark it as answer, but I'll give a +1 :); thanks.. – Natan Jan 25 '15 at 18:09
  • I'll update when I get some time with the InjectionResolver example. – Paul Samsotha Jan 25 '15 at 18:24
  • I take that back. After taking some time to think about how to go about replicating the Guice functionality, I realized it is not a trivial task to replicate in a universal way. There is over 1000 lines of code in Guice to create this fucntionality for the `FactoryModuleBuilder` API. It involves a whole lot of reflection. To create the functionality for a single use case is not so complicated, but it still involves a lot more than what you have in your answer. It wouldn't make sense to to over complicate things by using the framework API when you don't need to. – Paul Samsotha Jan 26 '15 at 04:38
  • I'd say if you're using this functionality alot, maybe consider using the bridge just to take advantage of this feature. You can still use HK2 for everything else. If you don't use it that often, you're solution maybe be the easiest to implement for a single use case. – Paul Samsotha Jan 26 '15 at 04:40
  • Yeah, on second thought, it's not such a big trouble to implement my factory class. I was approaching the problem wrong and was focusing to find a way to replicate the feature while trying to solve it manually was just fine. I'll mark mine as answer then. Thanks a lot @peeskillet. – Natan Jan 26 '15 at 08:05
  • gross. why does java need 14 different implementations of everything? DI frameworks, connection pools, logging, etc. etc. – stantonk Sep 22 '15 at 04:48