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

Pierre De Rop commented on FELIX-5336:
--------------------------------------

Some various enhancements was done for the dependency manager here 
[https://github.com/pderop/dm.enhanced]., and this github project also contains 
a simple support for service scopes (I plan to commit all this for the next r12 
dm release soon).

So, here is a description of the what has been done in the github project:

>From the provider side, a new "scope" parameter has been added in the 
>Component interface, allowing to define the scope of the registered service, 
>and the parameter has three enum values: SINGLETON, BUNDLE, PROTOTYPE
 * SINGLETON: it's as before: your registered service is a singleton
 * BUNDLE: the service will be registered as a _ServiceFactory_
 * PROTOTYPE: the service will be registered as a _PrototypeServiceFactory_

Scoped Services are supported by all kind of DM service components:
 * Component
 * Aspect Service
 * Adapter Service
 * Factory Pid Service
 * Bundle Adapter service

When a consumer requests a service (using ServiceDependency), then DM will 
automatically dereference the service like this:
 * if the service has a SERVICE_SCOPE service property matching 
SCOPE_PROTOTYPE, the DM will internally dereference the service using the 
ServiceObject API: so, the consumer will get its own copy of the requested 
service instance.
 * else, DM will internally dereference the service, as before.

When defining scoped component implementation, you can optionally define two 
special class fields in order to get injected with the client Bundle requesting 
the service, and the ServiceRegisgtration Object. Using the DM API this is 
enabled by default and you can turn off auto config using 
Component.setAutoConfig(Bundle.class, boolean) method, or 
Component.setAutoConfig(ServiceRegistration.class, boolean) methods. When using 
annotation, just use @Inject annotations in order to get injected with the 
client Bundle or the ServiceRegistration. You can also define a constructor 
which takes as argument the client bundle as well as the service registration, 
and in this case auto configuring Bundle/ServiceRegistration in class fields 
will be disabled. (we will give concrete examples bellow using DM native API 
and DM annotations).

*Example using annotations:*

here is a MyService component with PROTOTYPE scope, and each requester will get 
its own copy of a MyService instance, and the MyServiceImpl.start() method will 
be called for each instance:
{code:java}
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.ServiceScope;

@Component(scope=ServiceScope.PROTOTYPE)
public class MyServiceImpl implements MyService {
    @Start
    void start() {
        // called on each MyService instance
    }
}{code}
The above service will then automatically be instantiated for each of the 
following service requesters:
{code:java}
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.ServiceScope;

@Component
public class Client1 {
    @ServiceDependency
    void bind(MyService service) {
    // Client1 will be injected with its own MyService instance
}

@Component
public class Client2 {
    @ServiceDependency
    void bind(MyService service) {
    // Client2 will be injected with its own MyService instance
    }
}
{code}
The two Client1/Client2 above will be injected with two distinct component 
instances for the MyService service (each MyServiceImpl instance will be 
invoked in its start callback). Now, if you want to control the creation of the 
MyService, you can then define a bind method which takes as argument a 
ServiceObjects parameter like this:
{code:java}
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.ServiceScope;

@Component
public static class Client {
    @ServiceDependency
    void bind(ServiceObject<MyService> serviceObjects) {
        MyService service;
        try {
            service = serviceObjects.getService();
        } finally {
            serviceObjects.ungetService(service);
        }
    }
}
{code}
Note that, unlink in DS, the ServiceObjects is not wrapped and won't 
auto-unregister the created services when the bundle is stopping (I did not 
have time to do this). Internally, DM will use the 
PrototypeServiceFactory.getService(Bundle clientBundle, ServiceRegistration 
reg) method in order to instantiate the MyServiceImpl component. So, the 
MyServiceImpl component can optionally use the @Inject annotation in order to 
get injected with the clientB undle and/or the service regisration, like this:
{code:java}
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.ServiceScope;

@Component(scope=ServiceScope.PROTOTYPE)
public static class MyServiceImpl implements MyService {

    @Inject
    Bundle m_clientBundle;

    @Inject
    ServiceRegisration m_registration;

    @Start
    void start() {
    // called on each MyService instance.
    }
}{code}
The Bundle and ServiceRegistration can also be injected in the component 
Constructor:
{code:java}
import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.ServiceScope;

@Component(scope=ServiceScope.PROTOTYPE)
public static class MyServiceImpl implements MyService {
   public MyServiceImpl(Bundle clientBundle, ServiceRegistration registration) 
{ 
      ... 
   }
    
   @Start
   void start() {
           // called on each MyService instance.
        }
}{code}
*Example using DM API:*

So, here is a MyService component with PROTOTYPE scope, and each requester will 
get its own copy of MyService instance (the MyServiceImpl.start() method will 
be called for each MyServiceImpl instance):
{code:java}
public class Activator extends DependencyActivatorBase {
    @Override
    public void init(BundleContext context, DependencyManager dm) throws 
Exception {
        dm.add(createComponent()
            .setScope(ServiceScope.PROTOTYPE)
            .setInterface(MyService.class.getName(), null)
            .setImplementation(MyServiceImpl.class));
    }
}

public class MyServiceImpl implements MyService {
    void start() {
        // called on each MyService instance
    }
}
{code}
The MyServiceImpl, like with annotations, can define a constructor in order to 
be injected with the client bundle invoking the service and also the service 
Registration:
{code:java}
public class MyServiceImpl implements MyService {
    public MyServiceImpl(Bundle clientBundle, ServiceRegistration reg) { ... }
    void start() {
        // called on each MyService instance
    }
}
{code}
If you want to auto configure the client Bundle, and the ServiceRegistration, 
then simply define class fields, they will be auto injected, unless you disable 
auto configuraiton for Bundle/ServiceRegistration using 
Component.setAutoConfig(Class, boolean) methods.

Now, here is a Client component which simply depends on the MyService service 
using a basic DM activator (nothing special to do):
{code:java}
public class Activator extends DependencyActivatorBase {
    @Override
    public void init(BundleContext context, DependencyManager dm) throws 
Exception {
        dm.add(createComponent()
            .setImplementation(Client.class)
            .add(createServiceDependency()
                 .setService(MyService.class, 
null).setRequired(true).setCallbacks("bind", "unbind"));
    }
}

public class Client {
    void bind(MyService service) {
        // our client is injected with a specific instance of the MyService 
component 
        // that is created for our Client.
        // If another component defines a service dependency on MyService, then 
the other 
        // component will get its own private copy of MyService component 
instance
    }
}
{code}
*Example using ServiceObjects API:*

If now you want to control the creation of the MyService using raw OSGI 
ServiceObjects API, you can also do it like this:
{code:java}
public class Client {
    void bind(ServiceObjects<MyService> so) {
        MyService s1 = so.getService();
        MyService s2 = so.getService();
        ...
        so.ungetService(s1); // will deactivate the MyService s1 instance
        so.ungetService(s2); // will deactivate the MyService s2 instance
    }
}
{code}
 
h4.  
h4.  

> 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-r12
>            Reporter: Pierre De Rop
>            Assignee: Pierre De Rop
>            Priority: Major
>         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/[email protected]/msg17473.html



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to