Aloha! :) On Oct 5, 2011, at 9:21 PM, Bram de Kruijff wrote:
> Hi gain :) > > On Wed, Oct 5, 2011 at 2:26 PM, Marcel Offermans > <[email protected]> wrote: >> Hello Bram, >> >> On Oct 5, 2011, at 1:36 PM, Bram de Kruijff wrote: >> >>> 2011/10/4 Marcel Offermans <[email protected]>: >>>> Hello Bram, >>>> On Oct 4, 2011, at 9:06 PM, Bram de Kruijff wrote: >>>> >>>> On Tue, Oct 4, 2011 at 2:40 PM, Marcel Offermans >>>> <[email protected]> wrote: >>>> >>>> Summarizing, I hope we can agree that: >>>> >>>> a) ultimately, a multi-container model is what we aim for >>>> >>>> yup >>>> >>>> b) for now, we need to carefully plan a migration strategy >>>> >>>> I have no clue how to do this (make it optional) in a non >>>> multi-container model. Do you have an idea? >>>> >>>> Yes. Let's start with the assumption that our service implementations are >>>> POJOs, that implement some service interface (I left out the import >>>> statements on purpose): >>>> package api; >>>> public interface MyService { >>>> } >>>> package impl; >>>> public class MyServiceImpl implements MyService { >>>> } >>>> The OSGi "glue" we need to make this work is: >>>> package impl.osgi; >>>> public class Activator extends DependencyActivatorBase { >>>> public void init(BundleContext context, DependencyManager manager) throws >>>> Exception { >>>> manager.add(createComponent() >>>> .setInterface(MyService.class.getName(), null) >>>> .setImplementation(MyServiceImpl.class) >>>> ); >>>> } >>>> public void destroy(BundleContext context, DependencyManager manager) >>>> throws >>>> Exception { >>>> } >>>> } >>>> Now in general, our tenant aware version of this service uses a different >>>> activator (because it's based on an adapter) and the POJO is slightly >>>> extended with a bit of extra code (we could even argue here that this code >>>> does not belong in the POJO at all, but I'll leave that for now). So we get >>>> an implementation that looks like this: >>>> package impl.tenant; >>>> public class MyServiceTenantImpl extends MyServiceImpl { >>>> volatile Tenant m_tenant; >>>> >>>> public void init(Component component) { >>>> >>>> // setup tenant specific dependencies and code here >>>> } >>>> } >>>> The code in the init method usually adds dependencies to the component, and >>>> bases them on the injected Tenant. Finally, our activator looks like this: >>>> package impl.tenant.osgi; >>>> public class Activator extends DependencyActivatorBase { >>>> public void init(BundleContext context, DependencyManager manager) throws >>>> Exception { >>>> manager.add(createAdapterService(Tenant.class, null) >>>> .setInterface(MyService.class.getName(), null) >>>> .setImplementation(MyServiceTenantImpl.class) >>>> ); >>>> } >>>> public void destroy(BundleContext context, DependencyManager manager) >>>> throws >>>> Exception { >>>> } >>>> } >>>> So now we have all the code we need to create both alternatives, and we >>>> could package this as two separate bundles. One is completely unaware of >>>> tenants, and can therefore be used in any OSGi container. The other uses >>>> our >>>> current tenant model and is very similar to what we have now. My gut >>>> feeling >>>> is that 95% of the work will go into MyServiceImpl where maybe the only >>>> location where we might have a bit of duplication is in the definition of >>>> the dependencies (which for the tenant aware service will be in the init >>>> and >>>> for the tenant unaware one be in the Activator). >>> >>> The notion of seperating osgi/dm plumbing from the pojo is something >>> I'd really like! Using inheritance and being forced to manage seperate >>> artifacts not so much. >> >> The inheritance part is quite easy to fix. That was a shortcut I took to not >> complicate the first attempt at a solution too much. More below... >> >> The separate artifacts are a different case. If I do not want tenant >> support, I ideally do not want to depend on any form of tenant API as well. >> That means making those imports optional, and dealing with class not found >> in several locations. Even that is something we can do, provided we can >> agree that the POJO cannot use the Tenant either (which, again, it might not >> need if we can separate the plumbing from the implementation better). >> >>> Two thoughts: >>> >>> 1) The DM adaptor is part of the problem as it has a fixed strategy. I >>> could do the work myself with a custom factory component that can have >>> multiple strategies based on <any condition> like configuration, >>> system property or whatever. Eg. >>> >>> {code} >>> class MyServiceFactoryImpl implements MyServiceFactory { >>> void init(Component component){ >>> if(m_strategy == Strategy.ADAPTOR) >>> // start tracking services >>> } >>> void start(Component component){ >>> if(m_strategy == Strategy.SINGLETON) >>> // create/configure new MyService singleton >>> } >>> void stop(Component component){ >>> // remove MyService instances >>> } >>> void serviceAdded(ServiceReference ref){ >>> // create/configure new MyService instance >>> } >>> void serviceRemoved(ServiceReference ref){ >>> // removed new MyService instance >>> } >>> } >>> {code} >>> >>> Now I have one implementation that can do both. Not sure if we could >>> internalize this in DM? With some fancy generics and a base factory >>> the default case would be simple. >> >> The generics are out, as DM should be able to run on the OSGi minimum >> execution environment (subset of Java 1.4). >> >> This idea, in a slightly different form, is possible. What is happening is >> that the init/start/stop/destroy life cycle methods are being used to "setup >> dependencies" which effectively is plumbing and does not belong in the POJO. >> If you want to use these life cycle methods for purposes like this, it is >> possible to define an instance that is being invoked whenever the DM wants >> to invoke a life cycle method on an instance (see >> Component.setCallback(instance, init, start, stop, destroy)). This "callback >> handler" can do something like this: >> >> class Handler { >> public void init(Component c) { >> // setup dependencies for c >> // maybe even invoke an init method on the instance belonging to c, maybe >> not >> } >> // etc... >> } > > True, but I think I remember this does not cover the updated() for > configurationDependecies yet or am I confused? I'll have to try it > out. You're right, it does not, only the life cycle methods. If configuration is considered to be plumbing by a component, we should address that separately (probably as part of FELIX-2706). I'll give that some thought tomorrow. >> Solving the optional import requires some more code and thought, as does >> coming up with a "smart" activator that can automatically detect if tenants >> are available and/or required or not. >> >> I'm still not convinced if it makes sense to try to integrate the two into >> one artifact, especially if we eventually aim to go for a different tenant >> implementation anyway. > > I can see it work both ways. I'm under the assmption that must mt > services will have the import anyway as they do something with the > Tenant. It's a somewhat similar discussion to configuration / ManagedService.updated(). If it's just plumbing, you can probably read the tenant information in the "init" method and pass it on to the instance. If not, ... :) >>> 2) Using the DM is still invasive in that I still have an >>> init(component c) in my POJO service for all but the simplest cases. >> >> Solved with the solution above. >> >>> Again I think the factory could help in it where allowed to handle the >>> DM callbacks/lifecycle. This is kind of similar to what I requested >>> for configuration at https://issues.apache.org/jira/browse/FELIX-2706. >>> It kind of like DS I guess. >> >> OSGi defines this as a compendium service, which your POJO implements just >> like any other service. If you look at it from that point of view, why >> bother hiding it? But I can see what you mean and I think with a bit of code >> you can probably either write a "different" configuration dependency that >> does allow callbacks, or extend the current one to support an optional >> callback instance to validate the configuration. The question still >> rempains: what if your configuration *is* needed as part of your business >> logic and not just for plumbing? >> >>> Obviously, you will still have the <adapted class> compile dependency >>> even if you don't use the adapter strategy... >> >> See above, that would have to become an optional import in such a scenario. >> >> Summarizing: >> >> Trying to keep all plumbing out of the POJO makes a lot of sense. If needed >> I will even extend support for that (I know there's some donation coming in >> this area). >> >> Trying to create one artifact, with optional imports and logic to deal with >> both cases still does not make a lot of sense to me when I look at where we >> want to go in the future. I would prefer to keep both artifacts separated, >> let the user decide which one he wants to use, and in the future, deprecate >> one of them. > > Guess that could work for me! Guess we just have try and implement it > a couple of time. Unsure how hard it is to write POJO services that > can act both single and multi tenant. I agree that we should probably just have a go at it, see how it works out. We could start with some of the core/web stuff. Probably makes sense to branch and do that in a sandbox. I don't mind having a go at this tomorrow. > Think we need to write some of this stuff down in guidelines. Definitely, once we fleshed out all the details. Greetings, Marcel _______________________________________________ Amdatu-developers mailing list [email protected] http://lists.amdatu.org/mailman/listinfo/amdatu-developers

