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]

