javajersey-2.0hk2jersey-test-framework

Jersey Test + HK2 + Inject Factory service


I am trying to inject a object provided by HK2 Factory service in a Jersey Test Class but getting unsatisfied dependencies exception.

I have a factory service as below

    public class TestFactory implements Factory<TestObject>{

        private final CloseableService closeService;

        @Inject
        public TestFactory(CloseableService closeService) {
            this.closeService = closeService;
        }

        @Override
        public TestObject provide() {
            TestObject casualObject = new TestObject();
            this.closeService.add(() -> dispose(casualObject));
            return casualObject;
        }

        @Override
        public void dispose(TestObject instance) {
            instance.destroy();
        }
    }

And a Jersey Test class

    public class SampleTestCass extends JerseyTestNg.ContainerPerClassTest
    {

      //@Inject
      private TestObject myTestObject;

      private ServiceLocator locator;


     @Override
        protected Application configure()
        {
            ResourceConfig resConfig = new ResourceConfig(MyApi.class);
            resConfig.register(getBinder());
            locator = setupHK2(getBinder());

            return resConfig;
        }

        // setup local hk2
        public setupHK2(AbstractBinder binder) 
        {
            ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
            ServiceLocator locator = factory.create("test-locator");

            DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
            DynamicConfiguration dc = dcs.createDynamicConfiguration();

            locator.inject(binder);
            binder.bind(dc);
            dc.commit();
            return locator;
        }

        // get the binder
        public AbstractBinder getBinder()
        {
        return new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(TestFactory.class, Singleton.class).to(TestObject.class).in(PerLookup.class);
                }

            }
        }

         @BeforeClass
        public void beforeClass()
        {
            myTestObject = locator.getService(TestObject.class);

            // use myTestObject
        }

         @AfterClass
        public void afterClass()
        {
            if (locator != null) {
                locator.shutdown();
            }
        }

        @Test()
        public void someTest()
        {
            // some test code...
        }

    }

And getting below exceptions

A MultiException has 3 exceptions. They are:

  1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CloseableService,parent=TestFactory,qualifiers={},position=0,optional=false,self=false,unqualified=null,2053349061)
  2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.test.factories.TestFactory errors were found
  3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.test.factories.TestFactory

Solution

  • CloseableService is a service available within a Jersey application. The ServiceLocator you created is not tied to the Jersey application. It is just a standalone locator. So trying to register the TestFactory with this locator will cause it to fail, as there is no CloseableService. The one that you registered with the ResourceConfig will work just fine.

    Not sure what exactly you're trying to do, but if you want access to the service inside the test, one thing you can do is just bind the service as an instance, something like

    class MyTest {
        private Service service;
    
        @Override
        public ResourceConfig configure() {
            service = new Service();
            return new ResourceConfig()
                .register(new AbstractBinder() {
                    @Override
                    public void configure() {
                        bind(service).to(Service.class);
                    }
                })
        }
    }