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
<[email protected]> 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
>> <[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]
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to