Search code examples
jersey-2.0hk2

HK2 Factory Implementation Singleton


I am trying to understand HK2 Factory implementation in Jersey Application.

Goal : How to implement singleton factory?

    // Below is the simple factory implementation

    public class MyFactory implements Factory<SomeObject> {

        private static final Logger logger = LoggerFactory.getLogger(MyFactory.class);
        private final CloseableService closeService;

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

        @Override
        public MyFactory provide() {
            logger.debug("provide object from MyFactory");
            SomeObject objectFromFactory = new SomeObject();
            this.closeService.add(() -> dispose(objectFromFactory));
            return objectFromFactory;
        }

        @Override
        public void dispose(SomeObject instance) {
            // destroy instance
             logger.debug("dispose object from MyFactory");
        }
    }

    // and binding
     bindFactory(MyFactory.class).to(SomeObject.class).in(Singelton.class);

     // to check object creation and destruction
     bind(HK2InstanceListener.class).to(InstanceLifecycleListener.class).in(Singleton.class);

    // and injecting in some resource class

    @Inject
    SomeObject useThisObject;

    //updated information 

    AbstractBinder abstractBinder = configureBinder();

    ServiceLocator appServiceLocator = configureHK2(abstractBinder);

    public static AbstractBinder configureBinder() {
            return new AbstractBinder() {
                @Override
                protected void configure() {
         bindFactory(MyFactory.class).to(SomeObject.class).in(Singelton.class);

           // to check object creation and destruction
         bind(HK2InstanceListener.class).to(InstanceLifecycleListener.class).in(Singleton.class);

         }
         }

    public ServiceLocator configureHK2(AbstractBinder binder) {
            ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
            ServiceLocator locator = factory.create("my-test-server");

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

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

On starting application I see below in the logs

    10:38:34.122 [grizzly-http-server-0] DEBUG com.test.HK2InstanceListener - HK2 before object create : com.test.MyFactory
    10:38:34.125 [grizzly-http-server-0] DEBUG com.test.HK2InstanceListener - HK2 before object create : com.test.MyFactory
    10:38:34.125 [grizzly-http-server-0] DEBUG com.test.HK2InstanceListener - HK2 after object create : com.test.MyFactory
    10:38:34.125 [grizzly-http-server-0] DEBUG com.test.MyFactory provide - object from MyFactory
    10:38:35.700 [grizzly-http-server-0] DEBUG com.test.HK2InstanceListener - HK2 after object create : com.test.MyFactory

    10:38:37.743 [grizzly-http-server-0] DEBUG com.test.MyFactory - dispose object from MyFactory
  1. When scope = Singleton

    • creating two objects of MyFactory
    • Next request fails with null pointer exception @Inject.
  2. When scope = RequestScoped, PerLookup

    • Every request creates two objects of MyFactory

By Singleton factory, I understand is, a single object of factory (MyFactory) which provides some kind of objects upon injection.

So (1) should work or am I missing something?

And Why two objects of Factory?

Any suggestions? Thanks in advance.

HK2 Version : 2.5.0-b60
Jersey Version: 2.26

Additional Information on NPE

It was not from HK2 but behavior differs between .in(Singleton.class) and .in(PerLookup.class)

// SomeObject looks like

Class SomeObject
{

    private Stack<String> someStack; 

     public SomeObject() {

        // this may be the issue for Singleton
        this.someStack = new Stack();
    }

    public someOperation(String stackIt)
    {
        // NPE location
        this.someStack.push(stackIt);
    }
}

NPE at above location when below

bindFactory(MyFactory.class,Singleton.class).to(SomeObject.class).in(Singleton.class);

No NPE at above location when below

bindFactory(MyFactory.class,Singleton.class).to(SomeObject.class).in(PerLookup.class);

Solution

  • When you do your bindFactory use the second argument to bind the Factory as a Singleton. The way you are doing it only the provide method is bound as a Singleton. So to make the Factory itself also a Singleton do it like this:

    bindFactory(MyFactory.class, Singleton.class).to(SomeObject.class).in(Singelton.class);
    

    That should get both the thing provided AND the Factory itself bound as singletons.