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 2018/09/23 22:26:00 UTC

[jira] [Commented] (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=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/users@felix.apache.org/msg17473.html



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