users@hk2.java.net

Dealing with circular dependencies in _at_Immediate scope

From: Mirko Raner <mirko.raner_at_lab49.com>
Date: Mon, 17 Nov 2014 18:15:13 -0500

Hi all,


I'm currently dealing with migrating some code from Guice to HK2, and we
are running into some troubles with circular dependencies in @Immediate
scope.

Please consider the following interfaces and implementations:


*interface* MessageHandler *{*

*}*



*interface* RepositoryClient *{*

*}*



*interface* ServiceClient *{*

*}*



*class* MessageHandlerImpl *implements* MessageHandler {



       *private* RepositoryClient *handler*;



       @Inject

       MessageHandlerImpl(RepositoryClient handler) {

              *this*.handler = handler;

       }

}



*class* RepositoryClientImpl *implements* RepositoryClient {



       *private* ServiceClient *client*;



       @Inject

       *public* RepositoryClientImpl(ServiceClient client) {

              *this*.client = client;

       }

}



*class* ServiceClientImpl *implements* ServiceClient {



       *private* MessageHandler *handler*;



       @Inject

       *void* registerHandler(MessageHandler *handler*) {

              *this*.handler = handler;

       }

}


In particular, please note that there is effectively a circular dependency
between the three implementation glasses. Not good, definitely a code
smell, but also not the end of the world, and, for purposes of this
discussion, let's just say it cannot easily be avoided.

In the existing Guice code, there are no problems with instantiating the
classes. Guice figures out the correct order to instantiate the classes and
to break the cycle; no proxies or other trickery are involved under the
hood, AFAICT.


To translate the Guice code to HK2, we replaced .asEagerSingletion() with
.in(Immediate.class). It is my understanding that this is HK2's closest
equivalent of eager singletons.

Now, if we try to instantiate the objects with HK2, we run into a deadlock
inside the ImmediateHelper class. The following JUnit test demonstrates the
issue; Guice produces the correct object graph, whereas HK2 times out:


*import* org.glassfish.hk2.api.Immediate;

*import* org.glassfish.hk2.api.ServiceLocator;

*import* org.glassfish.hk2.utilities.ServiceLocatorUtilities;

*import* org.glassfish.hk2.utilities.binding.AbstractBinder;

*import* org.junit.Test;

*import* com.google.inject.AbstractModule;

*import* com.google.inject.Guice;

*import* com.google.inject.Injector;

*import* *static* org.junit.Assert.*assertNotNull*;



*public* *class* CircularDependencyInjectionHK2vsGuice {



       @Test(timeout=5000)

       *public* *void* testHK2Immediate() {

              ServiceLocator serviceLocator = ServiceLocatorUtilities.
*createAndPopulateServiceLocator*();

              ServiceLocatorUtilities.*enableImmediateScope*(serviceLocator
);

              AbstractBinder binder = *new* AbstractBinder() {

                     @Override

                     *protected* *void* configure() {

                           bind(ServiceClientImpl.*class*).to(ServiceClient.
*class*).in(Immediate.*class*);

                           bind(MessageHandlerImpl.*class*
).to(MessageHandler.*class*).in(Immediate.*class*);

                           bind(RepositoryClientImpl.*class*
).to(RepositoryClient.*class*);

                     }

              };

              ServiceLocatorUtilities.*bind*(serviceLocator, binder);

              MessageHandler messageHandler = serviceLocator
.getService(MessageHandler.*class*);

              ServiceClient serviceClient = serviceLocator
.getService(ServiceClient.*class*);

              *assertNotNull*(messageHandler);

              *assertNotNull*(serviceClient);

       }



       @Test(timeout=5000)

       *public* *void* testGuiceEagerSingleton() {

              AbstractModule module = *new* AbstractModule() {

                     @Override

                     *protected* *void* configure() {

                           bind(ServiceClient.*class*).to(ServiceClientImpl.
*class*).asEagerSingleton();

                           bind(MessageHandler.*class*
).to(MessageHandlerImpl.*class*).asEagerSingleton();

                           bind(RepositoryClient.*class*
).to(RepositoryClientImpl.*class*);

                     }

              };

              Injector injector = Guice.*createInjector*(module);

              MessageHandler messageHandler = injector
.getInstance(MessageHandler.*class*);

              ServiceClient serviceClient = injector
.getInstance(ServiceClient.*class*);

              *assertNotNull*(messageHandler);

              *assertNotNull*(serviceClient);

       }

}



Is this a bug in HK2's Immediate resolution code? Should I file a JIRA
issue?

Or am I doing something else wrong that sends HK2 into a deadlock?


Thanks,


Mirko

-- 
 
This email and any attachments may contain information which is 
confidential and/or privileged. The information is intended exclusively for 
the addressee and the views expressed may not be official policy, but the 
personal views of the originator. If you are not the intended recipient, be 
aware that any disclosure, copying, distribution or use of the contents is 
prohibited. If you have received this email and any file transmitted with 
it in error, please notify the sender by telephone or return email 
immediately and delete the material from your computer. Internet 
communications are not secure and Lab49 is not responsible for their abuse 
by third parties, nor for any alteration or corruption in transmission, nor 
for any damage or loss caused by any virus or other defect. Lab49 accepts 
no liability or responsibility arising out of or in any way connected to 
this email.