You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@felix.apache.org by Bram de Kruijff <bd...@gmail.com> on 2011/01/03 10:57:58 UTC

DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Hi list,

question/observation about the dependencymanager callbacks contract. I
noticed that init() was being invoked more then once over the lifespan
of my service instance because 1) I constructed it with an instance
(insted of class/factory) and 2) I was adding dependencies in init,
leading to duplicates, leading to multiple service dependecy
callbacks.

  // Eg. This will result in multiple service dependencies upon
logservice (un)availibility as we will go throught init/destroy each
cycle
  public synchronized void init() {
        ServiceDependency logServiceDependency =
m_dependencyManager.createServiceDependency();
        logServiceDependency.setService(LogService.class);
        logServiceDependency.setRequired(true);
        m_component.add(logServiceDependency);
   }

My (naive) assumption was that init() would be called once and only
once an a service instance, but as it seems init/destroy is called
each time a required service becomes (unavailable). An assumption
corroborated by the following DependecyManager ComponentImpl snippet.

        if (oldState.isBound() && newState.isWaitingForRequired()) {
            m_executor.enqueue(new Runnable() {
                public void run() {
                    unbindService(oldState);
                    deactivateService(oldState);
                }});
        }

Question is why and what is the contract? Obvisouly, the fact that
this results in different behaviour depending on how one instantiates
the Component is rather confusing :S

Regards,
Bram

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Re: DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Posted by Marcel Offermans <ma...@luminis.nl>.
On Jan 3, 2011, at 13:25 , Bram de Kruijff wrote:

> Ah yes! That was the behaviour I was looking for, looks good :) One
> final question:
> 
> Assuming one honours the start/stop contract and memory concerns
> aside. Why would one choose not te make a dependency
> instanceBound(true)?

No reason other than memory consumption as it will keep the instance around once it's created.

Greetings, Marcel


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Re: DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Posted by Bram de Kruijff <bd...@gmail.com>.
Ah yes! That was the behaviour I was looking for, looks good :) One
final question:

Assuming one honours the start/stop contract and memory concerns
aside. Why would one choose not te make a dependency
instanceBound(true)?

Thanks,
Bram

On Mon, Jan 3, 2011 at 12:49 PM, Marcel Offermans
<ma...@luminis.nl> wrote:
> Hello Bram,
>
> On Jan 3, 2011, at 11:54 , Bram de Kruijff wrote:
>
>> On Mon, Jan 3, 2011 at 11:09 AM, Marcel Offermans
>> <ma...@luminis.nl> wrote:
>>> On Jan 3, 2011, at 10:57 , Bram de Kruijff wrote:
>>>
>>>> question/observation about the dependencymanager callbacks contract. I
>>>> noticed that init() was being invoked more then once over the lifespan
>>>> of my service instance because 1) I constructed it with an instance
>>>> (insted of class/factory)
>>>
>>> Indeed, if you construct the component and supply an already created instance, the dependency manager will use that instance every time instead of constructing a new one itself every time the components needs to be created. So if you create an instance yourself, make sure it can handle multiple life cycles.
>>>
>>>> and 2) I was adding dependencies in init,
>>>> leading to duplicates, leading to multiple service dependecy
>>>> callbacks.
>>>
>>> If you add dependencies in init, you run the risk of your component getting deactivated immediately. Since that can lead to complicated logic in your component, you should use setInstanceBound(true) for such components, which ensures that if an instance was already created, it will not go away again, not even when a required dependency is not available. The instance will just sit there and wait for that dependency to show up again.
>>
>> Sorry but I do not get the setInstanceBound(true) thing. It seems you
>> can set it on a Dependecy but what does it mean? Does "it will not go
>> away again" refer to the service instance or the service registration.
>> In case of the former, will it solve my problem as init wont be called
>> again? in case of the latter, it is like a require once and afterwards
>> it might be a NullObject?
>
> Using setInstanceBound(true) on a dependency means that if the component it belongs to was already instantiated and this dependency is (required but) not available to not immediately call "destroy" again on the instance but instead keep the instance alive.
>
> An example...
>
> Let's say we have a component C that has no dependencies initially:
>
> manager.add(createComponent().setImplementation(C.class));
>
> Now, in C we add a dependency in the init method, like you do:
>
> class C {
>  init(Component c) {
>    DependencyManager dm = c.getDependencyManager();
>    c.add(dm.createServiceDependency()
>      .setService(LogService.class)
>      .setRequired(true)
>    );
>  }
>  destroy() {
>    // do cleanup here
>  }
> }
>
> Let's also assume there is no LogService available in the service registry.
>
> What will happen is, the dependency manager will instantiate C because it has no dependencies. Directly after creating the new instance, the "init" method will be invoked. In this method, a new dependency will be added, which is both required and NOT available. That will cause the dependency manager to invoke "destroy" because no longer are all required dependencies available. Now, if you properly clean up after yourself, you would remove the dependency you just added in "init". However, that would cause an infinite loop as now the component would no longer have any dependencies. Not cleaning up is no good solution either, as it would mean that dependency would never go away. Even worse, each time "init" is invoked, you'd get one extra copy of the dependency.
>
> Now let's add setInstanceBound(true) to that dependency.
>
> What will happen now is that, after instantiating C and invoking init, the dependency manager will again realize that not all required dependencies are available. However, it will also see that the missing required dependency is "instance bound" which means it will NOT call "destroy" but instead keep the instance around.
>
> When, at a later point in time, a LogService becomes available, it will then call "start" on the instance (and if C had a service interface itself, it would add it to the service registry).
>
> So setting instance bound means your init method will only be called once on an instance, unless of course you created the instance yourself in which case you must always be prepared to handle more than one call to "init". However, in that case, calls will by symmetric in the sense that you will always see: "init" -> "destroy" -> "init", never "init" -> "init" (unless you don't have a destroy method declared).
>
> In general a component should be prepared to handle "init", then "start" -> "stop" (which may be repeated multiple times if there are instance bound dependencies) and finally "destroy". And if you give the dependency manager an instance yourself, that will be reused, so then after the final "destroy" you might get another "init" again.
>
>>>>  // Eg. This will result in multiple service dependencies upon
>>>> logservice (un)availibility as we will go throught init/destroy each
>>>> cycle
>>>>  public synchronized void init() {
>>>>        ServiceDependency logServiceDependency =
>>>> m_dependencyManager.createServiceDependency();
>>>>        logServiceDependency.setService(LogService.class);
>>>>        logServiceDependency.setRequired(true);
>>>>        m_component.add(logServiceDependency);
>>>>   }
>>>
>>> The other solution would be to immediately add such a dependency instead of dynamically adding it after the component is being activated.
>>
>> Yeah I know, but I just moved all "internal concerns" to init so this
>> is how I got there :) For now, using a factory also does the trick.
>
> I would still advise you to make the dependency instance bound in this case, to prevent it from being added multiple times as explained above.
>
>>> By the way, why do you make LogService a required dependency? In general I think that's a bad idea, since it will bring down your whole application if logging for some reason is not available (if you update the log bundle).
>>
>> Probably not the best example indeed :)
>
> :)
>
>>>> My (naive) assumption was that init() would be called once and only
>>>> once an a service instance, but as it seems init/destroy is called
>>>> each time a required service becomes (unavailable).
>>>
>>> [...]
>>>
>>>> Question is why and what is the contract? Obvisouly, the fact that
>>>> this results in different behaviour depending on how one instantiates
>>>> the Component is rather confusing :S
>>>
>>> See above, it will only if *you* supply an already created instance. In that case, I cannot assume I can create a new instance so I have to reuse the existing one. If you don't want that, either supply the class or a custom factory so I can invoke it to create an instance.
>>
>> Still confused... why go through init/destroy every time? What is the
>> logical distinction between init/start and stop/destroy if they all
>> get called every time. Or is there a case where you only hit
>> stop/start?  As the doc says you only go through start once all
>> required deps are present, I was expecting the reverse when one
>> disappears. Therefore was expecting just stop/start (aka bind/unbind)
>> upon required service (un)availibility.
>
> Init and destroy are there so you can dynamically add new dependencies to your component where those dependencies might be based on information you obtain from the set of dependencies you initially configure for your component.
>
> An example:
>
> Let's say you have a component that depends on a service called ComponentConfiguration. This service might have a method that is called useLogService() : boolean, and depending on what that service returns you might want to setup your component to either have a dependency on the OSGi LogService or not. In that case, in init you will already have access to ComponentConfiguration (which by then has already been injected, assuming you made it a required dependency) so you can invoke it's method and based on the result either add the extra dependency or not.
>
> Admitted, this might not be the best practical example, but you probably get the idea.
>
>> Trying to grasp the concepts here :)
>
> No problem, glad to (try to) explain it to you. :)
>
> Greetings, Marcel
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
> For additional commands, e-mail: users-help@felix.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Re: DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Posted by Marcel Offermans <ma...@luminis.nl>.
Hello Bram,

On Jan 3, 2011, at 11:54 , Bram de Kruijff wrote:

> On Mon, Jan 3, 2011 at 11:09 AM, Marcel Offermans
> <ma...@luminis.nl> wrote:
>> On Jan 3, 2011, at 10:57 , Bram de Kruijff wrote:
>> 
>>> question/observation about the dependencymanager callbacks contract. I
>>> noticed that init() was being invoked more then once over the lifespan
>>> of my service instance because 1) I constructed it with an instance
>>> (insted of class/factory)
>> 
>> Indeed, if you construct the component and supply an already created instance, the dependency manager will use that instance every time instead of constructing a new one itself every time the components needs to be created. So if you create an instance yourself, make sure it can handle multiple life cycles.
>> 
>>> and 2) I was adding dependencies in init,
>>> leading to duplicates, leading to multiple service dependecy
>>> callbacks.
>> 
>> If you add dependencies in init, you run the risk of your component getting deactivated immediately. Since that can lead to complicated logic in your component, you should use setInstanceBound(true) for such components, which ensures that if an instance was already created, it will not go away again, not even when a required dependency is not available. The instance will just sit there and wait for that dependency to show up again.
> 
> Sorry but I do not get the setInstanceBound(true) thing. It seems you
> can set it on a Dependecy but what does it mean? Does "it will not go
> away again" refer to the service instance or the service registration.
> In case of the former, will it solve my problem as init wont be called
> again? in case of the latter, it is like a require once and afterwards
> it might be a NullObject?

Using setInstanceBound(true) on a dependency means that if the component it belongs to was already instantiated and this dependency is (required but) not available to not immediately call "destroy" again on the instance but instead keep the instance alive.

An example...

Let's say we have a component C that has no dependencies initially:

manager.add(createComponent().setImplementation(C.class));

Now, in C we add a dependency in the init method, like you do:

class C {
  init(Component c) {
    DependencyManager dm = c.getDependencyManager();
    c.add(dm.createServiceDependency()
      .setService(LogService.class)
      .setRequired(true)
    );
  }
  destroy() {
    // do cleanup here
  }
}

Let's also assume there is no LogService available in the service registry.

What will happen is, the dependency manager will instantiate C because it has no dependencies. Directly after creating the new instance, the "init" method will be invoked. In this method, a new dependency will be added, which is both required and NOT available. That will cause the dependency manager to invoke "destroy" because no longer are all required dependencies available. Now, if you properly clean up after yourself, you would remove the dependency you just added in "init". However, that would cause an infinite loop as now the component would no longer have any dependencies. Not cleaning up is no good solution either, as it would mean that dependency would never go away. Even worse, each time "init" is invoked, you'd get one extra copy of the dependency.

Now let's add setInstanceBound(true) to that dependency.

What will happen now is that, after instantiating C and invoking init, the dependency manager will again realize that not all required dependencies are available. However, it will also see that the missing required dependency is "instance bound" which means it will NOT call "destroy" but instead keep the instance around.

When, at a later point in time, a LogService becomes available, it will then call "start" on the instance (and if C had a service interface itself, it would add it to the service registry).

So setting instance bound means your init method will only be called once on an instance, unless of course you created the instance yourself in which case you must always be prepared to handle more than one call to "init". However, in that case, calls will by symmetric in the sense that you will always see: "init" -> "destroy" -> "init", never "init" -> "init" (unless you don't have a destroy method declared).

In general a component should be prepared to handle "init", then "start" -> "stop" (which may be repeated multiple times if there are instance bound dependencies) and finally "destroy". And if you give the dependency manager an instance yourself, that will be reused, so then after the final "destroy" you might get another "init" again.

>>>  // Eg. This will result in multiple service dependencies upon
>>> logservice (un)availibility as we will go throught init/destroy each
>>> cycle
>>>  public synchronized void init() {
>>>        ServiceDependency logServiceDependency =
>>> m_dependencyManager.createServiceDependency();
>>>        logServiceDependency.setService(LogService.class);
>>>        logServiceDependency.setRequired(true);
>>>        m_component.add(logServiceDependency);
>>>   }
>> 
>> The other solution would be to immediately add such a dependency instead of dynamically adding it after the component is being activated.
> 
> Yeah I know, but I just moved all "internal concerns" to init so this
> is how I got there :) For now, using a factory also does the trick.

I would still advise you to make the dependency instance bound in this case, to prevent it from being added multiple times as explained above.

>> By the way, why do you make LogService a required dependency? In general I think that's a bad idea, since it will bring down your whole application if logging for some reason is not available (if you update the log bundle).
> 
> Probably not the best example indeed :)

:)

>>> My (naive) assumption was that init() would be called once and only
>>> once an a service instance, but as it seems init/destroy is called
>>> each time a required service becomes (unavailable).
>> 
>> [...]
>> 
>>> Question is why and what is the contract? Obvisouly, the fact that
>>> this results in different behaviour depending on how one instantiates
>>> the Component is rather confusing :S
>> 
>> See above, it will only if *you* supply an already created instance. In that case, I cannot assume I can create a new instance so I have to reuse the existing one. If you don't want that, either supply the class or a custom factory so I can invoke it to create an instance.
> 
> Still confused... why go through init/destroy every time? What is the
> logical distinction between init/start and stop/destroy if they all
> get called every time. Or is there a case where you only hit
> stop/start?  As the doc says you only go through start once all
> required deps are present, I was expecting the reverse when one
> disappears. Therefore was expecting just stop/start (aka bind/unbind)
> upon required service (un)availibility.

Init and destroy are there so you can dynamically add new dependencies to your component where those dependencies might be based on information you obtain from the set of dependencies you initially configure for your component.

An example:

Let's say you have a component that depends on a service called ComponentConfiguration. This service might have a method that is called useLogService() : boolean, and depending on what that service returns you might want to setup your component to either have a dependency on the OSGi LogService or not. In that case, in init you will already have access to ComponentConfiguration (which by then has already been injected, assuming you made it a required dependency) so you can invoke it's method and based on the result either add the extra dependency or not.

Admitted, this might not be the best practical example, but you probably get the idea.

> Trying to grasp the concepts here :)

No problem, glad to (try to) explain it to you. :)

Greetings, Marcel


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Re: DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Posted by Bram de Kruijff <bd...@gmail.com>.
He Marcel,

On Mon, Jan 3, 2011 at 11:09 AM, Marcel Offermans
<ma...@luminis.nl> wrote:
> On Jan 3, 2011, at 10:57 , Bram de Kruijff wrote:
>
>> question/observation about the dependencymanager callbacks contract. I
>> noticed that init() was being invoked more then once over the lifespan
>> of my service instance because 1) I constructed it with an instance
>> (insted of class/factory)
>
> Indeed, if you construct the component and supply an already created instance, the dependency manager will use that instance every time instead of constructing a new one itself every time the components needs to be created. So if you create an instance yourself, make sure it can handle multiple life cycles.
>
>> and 2) I was adding dependencies in init,
>> leading to duplicates, leading to multiple service dependecy
>> callbacks.
>
> If you add dependencies in init, you run the risk of your component getting deactivated immediately. Since that can lead to complicated logic in your component, you should use setInstanceBound(true) for such components, which ensures that if an instance was already created, it will not go away again, not even when a required dependency is not available. The instance will just sit there and wait for that dependency to show up again.

Sorry but I do not get the setInstanceBound(true) thing. It seems you
can set it on a Dependecy but what does it mean? Does "it will not go
away again" refer to the service instance or the service registration.
In case of the former, will it solve my problem as init wont be called
again? in case of the latter, it is like a require once and afterwards
it might be a NullObject?

>>
>>  // Eg. This will result in multiple service dependencies upon
>> logservice (un)availibility as we will go throught init/destroy each
>> cycle
>>  public synchronized void init() {
>>        ServiceDependency logServiceDependency =
>> m_dependencyManager.createServiceDependency();
>>        logServiceDependency.setService(LogService.class);
>>        logServiceDependency.setRequired(true);
>>        m_component.add(logServiceDependency);
>>   }
>
> The other solution would be to immediately add such a dependency instead of dynamically adding it after the component is being activated.

Yeah I know, but I just moved all "internal concerns" to init so this
is how I got there :) For now, using a factory also does the trick.

> By the way, why do you make LogService a required dependency? In general I think that's a bad idea, since it will bring down your whole application if logging for some reason is not available (if you update the log bundle).

Probably not the best example indeed :)

>> My (naive) assumption was that init() would be called once and only
>> once an a service instance, but as it seems init/destroy is called
>> each time a required service becomes (unavailable).
>
> [...]
>
>> Question is why and what is the contract? Obvisouly, the fact that
>> this results in different behaviour depending on how one instantiates
>> the Component is rather confusing :S
>
> See above, it will only if *you* supply an already created instance. In that case, I cannot assume I can create a new instance so I have to reuse the existing one. If you don't want that, either supply the class or a custom factory so I can invoke it to create an instance.

Still confused... why go through init/destroy every time? What is the
logical distinction between init/start and stop/destroy if they all
get called every time. Or is there a case where you only hit
stop/start?  As the doc says you only go through start once all
required deps are present, I was expecting the reverse when one
disappears. Therefore was expecting just stop/start (aka bind/unbind)
upon required service (un)availibility.

Trying to grasp the concepts here :)

Regards,
Bram

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org


Re: DependencyManager lifecycle callbacks contract? Aka multiple init() invokes in service impl

Posted by Marcel Offermans <ma...@luminis.nl>.
On Jan 3, 2011, at 10:57 , Bram de Kruijff wrote:

> question/observation about the dependencymanager callbacks contract. I
> noticed that init() was being invoked more then once over the lifespan
> of my service instance because 1) I constructed it with an instance
> (insted of class/factory)

Indeed, if you construct the component and supply an already created instance, the dependency manager will use that instance every time instead of constructing a new one itself every time the components needs to be created. So if you create an instance yourself, make sure it can handle multiple life cycles.

> and 2) I was adding dependencies in init,
> leading to duplicates, leading to multiple service dependecy
> callbacks.

If you add dependencies in init, you run the risk of your component getting deactivated immediately. Since that can lead to complicated logic in your component, you should use setInstanceBound(true) for such components, which ensures that if an instance was already created, it will not go away again, not even when a required dependency is not available. The instance will just sit there and wait for that dependency to show up again.

> 
>  // Eg. This will result in multiple service dependencies upon
> logservice (un)availibility as we will go throught init/destroy each
> cycle
>  public synchronized void init() {
>        ServiceDependency logServiceDependency =
> m_dependencyManager.createServiceDependency();
>        logServiceDependency.setService(LogService.class);
>        logServiceDependency.setRequired(true);
>        m_component.add(logServiceDependency);
>   }

The other solution would be to immediately add such a dependency instead of dynamically adding it after the component is being activated.

By the way, why do you make LogService a required dependency? In general I think that's a bad idea, since it will bring down your whole application if logging for some reason is not available (if you update the log bundle).

> My (naive) assumption was that init() would be called once and only
> once an a service instance, but as it seems init/destroy is called
> each time a required service becomes (unavailable).

[...]

> Question is why and what is the contract? Obvisouly, the fact that
> this results in different behaviour depending on how one instantiates
> the Component is rather confusing :S

See above, it will only if *you* supply an already created instance. In that case, I cannot assume I can create a new instance so I have to reuse the existing one. If you don't want that, either supply the class or a custom factory so I can invoke it to create an instance.

Greetings, Marcel


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@felix.apache.org
For additional commands, e-mail: users-help@felix.apache.org