You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@felix.apache.org by "Pierre De Rop (JIRA)" <ji...@apache.org> on 2016/12/10 17:27:58 UTC

[jira] [Comment Edited] (FELIX-5336) Add support for prototype scope services in DM4

    [ https://issues.apache.org/jira/browse/FELIX-5336?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15736700#comment-15736700 ] 

Pierre De Rop edited comment on FELIX-5336 at 12/10/16 5:27 PM:
----------------------------------------------------------------

Hi Jan Willem,

I have something working. can you please try it ?
I have attached to this issue FELIX-5336.tgz (it's not a diff, but a tar.tgz of the modified/added files).

so, to install, first checkout dm from the trunk, then cd to dependencymanager , and tar zxvf FELIX-5336.tgz
(the tar.gz contains some more fix for other issues , I will create more JIRA issues soon about them).

there is a demo in org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/servicescope/api/.

I'm now describing the model used (a bit different from the one initially described in this issue).

So, to create a scoped service component, you can now invoke the createComponent(ComponentPolicy) method.
The policy is an enum allowing to define a strategy about how components are created.
you can also use the policy for all adapters, and for aspects.

this enum contains the following (it is actually only used when the component is a service provider):

- ComponentPolicy.SINGLETON: -> singleton component, as usual.
- ComponentPolicy.BUNDLE -> an instance of the component must be created for each bundle using the service. it actually corresponds to a ServiceFactory
- ComponentPolicy.BUNDLE_DYNAMIC: same as ComponentPolicy.BUNDLE, but allows to define a component which can add some dynamic dependencies from its init/start callback.
- ComponentPolicy.PROTOTYPE -> an instance of the component must be created for each distinct request for the service. it actually corresponds to a PrototypeServiceFactory
- ComponentPolicy.PROTOTYPE_DYNAMIC: same as ComponentPolicy.PROTOTYPE, but allows to define a component which can add some dynamic dependencies from its init/start callback.


So, for BUNDLE, and PROTOTYPE, a hidden component will track dependencies, and will register a ServiceFactory, or a PrototypeServiceFactory, respectively. 

when a component instance is created (because there is a requester for the service), then the component will be injected with the bundle / service registration (if it defines a Bundle or a ServiceRegistration field). This parameters correspond to the ones passed to the actual Service ServiceFactory.getService (or PrototypeServiceFactory.getService) methods.

So, if your scoped service provider component is dynamic (it may add some dynamic dependencies from the init or start callback), then you can use one of the two
constants from the enum:

BUNDLE_DYNAMIC, or PROTOTYPE_DYNAMIC.

In this case, a prototype instance component is always created (singleton), it will then be called as a regular DM component, and will add any dynamic dependencies it is necessary to add from the init or start callbacks. And once fully started (all required + instance bound dependencies injected), then the corresponding ServiceFactory or PrototypeServiceFactory will then be registered. And when a requester will define a dependency on the service, a copy of the prototype component instance will be created and returned by the ServiceFactory.getService (or PrototypeServiceFactory.getService) methods. Notice that the prototype component instance (singleton) won't be injected with the "Bundle" or "ServiceRegistration" parameters. This will allow you to detect if your component is the prototype instance or if it's a clone being created and returned to the requesting service.

Note: I did not have time to implement smart ServiceObjects injection. I may do this in a later release.

I'm now describing the demo:

So, in this demo, you will find two kind of prototype services:


- a Provider service which has a PROTOTYPE scope: The Provider service 
does not add some dynamic dependencies from its init/start callbacks (else it would has
a PROTOTYPE_DYNAMIC scope). Two Consumer components are depending on the Provider, and each Consumer will get injected with its own copy of the Provider service.

- a DynamicProvider service: this component has a PROTOTYPE_DYNAMIC scope because it adds some dynamic dependencies from its init/start callbacks. So, here a DynamicProvider component instance will be created but won't be injected to any service requesters; instead of that the service consumer will be injected with a copy of the prototype instance. 
Now, the ConsumerUsingServiceObjects component depends on the DynamicProvider service. Unlike in the Consumer example, the ConsumerUsingServiceObjects component will be injected with a ServiceReference to the DynamicProvider, and the ConsumerUsingServiceObjects will then obtain an OSGi "ServiceObjects" in order to manually instantiates two DynamicProvider component instances.

When you run the DM shell, you will see the first "Provider" component like this:

{code}
 [1] Prototype(#2) for org.apache.felix.dependencymanager.samples.servicescope.api.Provider registered
    org.osgi.service.cm.ConfigurationAdmin service required available
{code}

here, we see that the Provider component is a Prototype and has two instances (see "Prototype(#2)").
There are two instances because there are two Consumer component depending on it.

and the DynamicProvider will be displayed like this:

{code}
 [6] Prototype(#2) for org.apache.felix.dependencymanager.samples.servicescope.api.DynamicProvider registered
    org.osgi.service.cm.ConfigurationAdmin service required available
{code}

There are two instances because the ConsumerUsingServiceObjects has created two instances of the DynamicProvider
component using the OSGI ServiceObjects API.


One important remark: when you are using ServiceReference, by default the Component for the ConsumerUsingServiceObjects  will internally acquire an instance of the DynamicProvider.

it was implemented like this so far, and for the moment I don't have added some complexity in the state machine in order to avoid dereferencing (getService()) the service even if you actually only need to get injected with a ServiceReference (and not with the service itself). So, in order to avoid having a DynamicProvider automatically dereferenced by the ConsumerUsingServiceObjects Component (internally), you will see in the demo Activator that I have introduced  a new "dereference(boolean)" method in the ServiceDependency interface: it allows you to tell to DM that you don't want your component to automatically dereference the injected service internally, because all you want is the just the ServiceReference, not the actual service. So, please for now, use the dereference(boolean) method. (see the Activator in the demo).

Please tell me what you think about all this, and I can then possibly make a release soon.
thanks a lot.


was (Author: pderop):
Hi Jan Willem,

I have something working. can you please try it ?
I have attached to this issue FELIX-5336.tgz (it's not a diff, but a tar.tgz of the modified/added files).

so, to install, first checkout dm from the trunk, then cd to dependencymanager , and tar zxvf FELIX-5336.tgz
(the tar.gz contains some more fix for other issues , I will create more JIRA issues soon about them).

now, in the org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/servicescope/api/, there is a demo.

I'm now describing the model used (a bit different from the one initially described in this issue).

So, to create a scoped service component, you can now invoke the createComponent(ComponentPolicy) method.
The policy is an enum allowing to define a strategy about how components are created.
you can also use the policy for all adapters, and for aspects.

this enum contains the following (it is actually only used when the component is a service provider):

- ComponentPolicy.SINGLETON: -> singleton component, as usual.
- ComponentPolicy.BUNDLE -> an instance of the component must be created for each bundle using the service. it actually corresponds to a ServiceFactory
- ComponentPolicy.BUNDLE_DYNAMIC: same as ComponentPolicy.BUNDLE, but allows to define a component which can add some dynamic dependencies from its init/start callback.
- ComponentPolicy.PROTOTYPE -> an instance of the component must be created for each distinct request for the service. it actually corresponds to a PrototypeServiceFactory
- ComponentPolicy.PROTOTYPE_DYNAMIC: same as ComponentPolicy.BUNDLE, but allows to define a component which can add some dynamic dependencies from its init/start callback.


So, for BUNDLE, and PROTOTYPE, a hidden component will track dependencies, and will register a ServiceFactory, or a PrototypeServiceFactory, respectively. 

when a component instance is created (because there is a requester for the service), then the component will be injected with the bundle / service registration (if it defines a Bundle or a ServiceRegistration field). This parameters correspond to the ones passed to the actual Service ServiceFactory.getService (or PrototypeServiceFactory.getService) methods.

So, if your scoped service provider component is dynamic (it may add some dynamic dependencies from the init or start callback), then you can use one of the two
constants from the enum:

BUNDLE_DYNAMIC, or PROTOTYPE_DYNAMIC.

In this case, a prototype instance component is always created (singleton), it will then be called as a regular DM component, and will add any dynamic dependencies it is necessary to add from the init or start callbacks. And once fully started (all required + instance bound dependencies injected), then the corresponding ServiceFactory or PrototypeServiceFactory will then be registered. And when a requester will define a dependency on the service, a copy of the prototype component instance will be created and returned by the ServiceFactory.getService (or PrototypeServiceFactory.getService) methods. Notice that the prototype component instance (singleton) won't be injected with the "Bundle" or "ServiceRegistration" parameters. This will allow you to detect if your component is the prototype instance or if it's a clone being created and returned to the requesting service.

In the demo (see org.apache.felix.dependencymanager.samples/src/org/apache/felix/dependencymanager/samples/servicescope/api/), you have a Provider which is a prototype scope service: it does not add some dynamic dependencies from its init/start callbacks. So, there is now two Client instances which get injected with their own copy of the ProviderImpl.

You also have a DynamicProvider: it is a scoped service (PROTOTYPE_DYNAMIC), and the prototype instance is first created in order to add a dynamic dependency from the init method.
and once started, then the corresponding PrototypeServiceFactory will be registered.

At this point, a ConsumerUsingServiceObjects component will be started, and in this example, unlike from the Client example, A ServiceReference to the DynamicProvider is injected. I did not have time to inject a ServiceObjects directly. So for now, if you need to manually create some instances, you have to use the ref in order to get the ServiceObjects. Then from the ConsumerUsingServiceObjects.start() method, two instances of the DynamicProvider component  are created, and deleted from the stop() method.

one important remark: when you are using ServiceReference, by default the Component for the ConsumerUsingServiceObjects  will aquire an instance of the DynamicProvider.
for the moment, I don't have added some complexity in the state machine in order to detect that you actually only need to get injected with a ServiceReference (and not with the service itself). So, in order to avoid having a DynamicProvider automatically dereferenced by the ConsumerUsingServiceObjects Component (internally), you will see in the demo Activator that I have introduced  a new "dereference(boolean)" method in the ServiceDependency interface: it allows you to tell to DM that you don't want your component to automatically dereference the injected service internally, because all you want is the just the ServiceReference, not the actual service. So, please for now, use the dereference(boolean) method. (see the Activator in the demo).

Please tell me what you think about all this, and I can then possibly make a release soon.
thanks a lot.

> Add support for prototype scope services in DM4
> -----------------------------------------------
>
>                 Key: FELIX-5336
>                 URL: https://issues.apache.org/jira/browse/FELIX-5336
>             Project: Felix
>          Issue Type: New Feature
>          Components: Dependency Manager
>    Affects Versions: org.apache.felix.dependencymanager-r8
>            Reporter: Pierre De Rop
>            Assignee: Pierre De Rop
>             Fix For: org.apache.felix.dependencymanager-r9
>
>         Attachments: FELIX-5336.tgz
>
>
> In the users mailing list, there is a wish to add support in DM4 for OSGi prototype scope services, which allows any service consumer to get its own instance of a given service dependency.
> See http://www.mail-archive.com/users@felix.apache.org/msg17473.html



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)