Hello Bram,

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

> On Mon, Jan 3, 2011 at 11:09 AM, Marcel Offermans
> <[email protected]> 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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to