users@hk2.java.net

Re: Dealing with circular dependencies in _at_Immediate scope

From: Mirko Raner <mirko.raner_at_lab49.com>
Date: Tue, 18 Nov 2014 10:25:39 -0500

Thanks for the prompt response, John!
Actually, this particular object graph can be instantiated without proxies
or providers:

   - ServiceClient s = new ServiceClientImpl();
   - RepositoryClient r = new RepositoryClientImpl(s);
   - MessageHandler m = new MessageHandlerImpl(r);
   - s.registerHandler(m);

It turns out that that's exactly what Guice ends up doing in this situation.
We do not have an ImmediateErrorHandler defined, but running the
application in the debugger revealed that ImmediateHelper is blocked on a
wait() in line 147, and the corresponding notify() is on the same thread.
BTW, we're using version 2.3.0-b09.

Mirko


On Mon, Nov 17, 2014 at 7:22 PM, john wells <john.wells_at_oracle.com> wrote:

> So, I don't even know how Guice could possibly break this cycle. There
> is no instance that can be created such that the incoming parameter can be
> initialized properly. That being said, hk2 should certainly NOT hang here,
> but instead should detect this very simple cycle. I'll look into that,
> because it should truly throw an exception in this case, almost
> immediately. (Actually, I wonder if it IS throwing an exception... do you
> have a ImmediateErrorHandler
> <https://hk2.java.net/2.4.0-b06/apidocs/org/glassfish/hk2/utilities/ImmediateErrorHandler.html>
> defined?)
>
> The proper way to fix this (and this is why JSR-330 invented Provider in
> the first place) is to use a Provider (and also, do NOT use provider.get()
> in your constructor, since that would also have the same cycle).
>
> Only one of your three implementation classes need to use it, so picking
> one at random to break the cycle, it'd be like this:
>
> *class* ServiceClientImpl *implements* ServiceClient {
>
>
>
> *private* Provider<MessageHandler> *handler*;
>
>
>
> @Inject
>
> *void* registerHandler(Provider<MessageHandler> *handler*) {
>
> *this*.handler = handler;
>
> }
> }
>
> Obviously, other fixes would include using proxies, but I agree that can
> get messy and bring in stuff you may not want to have there.
>
>
> On 11/17/2014 6:15 PM, Mirko Raner wrote:
>
> 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.
>
>
>

-- 
 
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.