javaosgiapache-felixdeclarative-services

Dynamically setting target property in OSGi Reference annotatation


After reading an article on Reference target (and corresponding properties), I still don't understand how to retrieve a service, when target is set at runtime (typically target and properties are set at compile time and evaluated by SCR at runtime).

Let's say there are three service implementations defining@Property(name="type", value="csv"), @Property(name="type", value="xls") and @Property(name="type", value="pdf"), respectively.

And one consumer with:

//@Reference(target="(type=%runtime_variable%)")
Service service;

NOTE %runtime_variable% is automatically evaluated at runtime (read from a settings file).


Should I just call getServiceReferences(Class<S> clazz, String filter) in the @Activate/@Modified annotated method in order to get the right service at runtime?

How is the component.xml created, if I don't explicitly use @Reference and dynamically set the target in the @Activate/@Modified annotated method?

And can I use the@Designate metatype annotation to make my life simpler here?


Solution

  • That article, you have read, is 7 year old and it is not clear to me which annotations it uses (yes, there are several). I'd suggest to ignore it. Today you better use Declarative Services (DS) and the standard OSGi annotations.

    In short there are 2 important pieces:

    While you can write the XML files by hand, they are typically generated by Bnd or other build tools using Bnd (such us bnd-maven-plugin). This is done at build time when Bnd inspects your classes for annotations and uses the information provided to generate the XML files. Thus the annotations are not used at all at runtime.

    As for the wiring, when you have

       @Reference(target="(type=pdf)")
       Service service;
    

    The field service will be automatically wired to one of the instances (yes there can be more than one) of Service service registered in OSGi's service registry that matches the target filter. This is done at runtime by SCR. You can change the target at runtime by reconfiguring your component using its PID. You can do that programmatically or via properties files using Configuration Admin.

    The @Designate annotation you mentioned relates to another OSGi specification called Metatype. It allows you to better define the types of the configuration fields. Here you can read more about how to use Metatype together with Declarative Services 1.3.

    Another good source of information regarding OSGi annotations is here (ignore the Liferay specific ones)


    To reflect your edited question, you have some options. One is to get all instances:

    @Reference(
     cardinality = ReferenceCardinality.MULTIPLE,
     policy = ReferencePolicy.DYNAMIC,
     policyOption = ReferencePolicyOption.GREEDY
     )
    protected void setService(Service service, Map<String, Object> properties) {
       String type = MapUtil.getString(properties, "type");
       _services.put(type, service);
    }
    

    Then you can get your service from _services map by type. Another is reconfigure your component. For example if you define it like this

    @Component(
     configurationPid = "my.component"
    )
    public class MyComponent implements ... {
       @Reference(target="(type=pdf)")
       Service myService;
    }    
    

    you can configure it via my.component.cfg in which you specify

    myService.target=(type=somethingElse)
    

    You can do the same programmatically using the Configuration Admin API.