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

Reply via email to