Question

I've been trying some examples with OSGi Declarative Services (among other things, such as Blueprint) on Karaf. The problem I am trying to solve now, is how to get references to certain services at runtime (so annotations and/or XML are not really an option here)

I will explain my use case:

I am trying to design (so far only in my head, that's why I am still only experimenting with OSGi :) ) a system to control certain automation processes in industry. To communicate with devices, a special set of protocols is being used. To make the components as reusable as possible, I designed a communication model based on layers (such as ISO/OSI model for networking, but much more simple)

To transform this into OSGi, each layer of my system would be composed of a set of bundles. One for interfaces of that layer, and then one plugin for each implementation of that layer (imagine this as TCP vs. UDP on the Transport layer of OSI).

To reference any device in such network, a custom address format will be used (two examples of such addresses can be xpa://12.5/03FE or xpb://12.5/03FE). Such address contains all information about layers and their values needed in order to access the requested device. As you can guess, each part of this address represents one layer of my networking model.

These addresses will be stored in some configuration database (so, again, simple .cfg or .properties files are not an option) so that they can be changed at runtime remotely.

I am thinking about creating a Factory, that will parse this address and, according to all its components, will create a chain of objects (get appropriate services from OSGi) that implement all layers and configure them accordingly.

As there can be more implementations of a single layer (therefore, more services implementing a single interface), this factory will need to decide, at runtime (when it gets the device address passed as string), which particular implementation to choose (according to additional properties the services will declare).

How could this be implemented in OSGi? What approach is better for this, DS, Blueprint or something else?

Was it helpful?

Solution

I realise that this is now a very late answer to this question, but both answers miss the obvious built-in support for filtering in Declarative Services.

A target filter can be defined for a DS reference using the @Reference annotation:

@Component
public class ExampleComponent {
    @Reference(target="(foo=bar)")
    MyService myService;
}

This target filter can also be added (or overriden) using configuration. For the component:

@Component(configurationPid="fizz.buzz")
public class ExampleComponent {
    @Reference
    MyService myService;
}

A configuration dictionary for the pid fizz.buzz can then set a new filter using the key myService.target.

This is a much better option than jumping down to the raw OSGi API, and has been available for several specification releases.

OTHER TIPS

I revoke my answer, because the acceppted answer is correct. When I answered that question I missed this little, but very important detail in the spec.

There is a nice way given by OSGi called service tracker. You can use inside a declarative service. In this example there is a config which holds the filter for the service you want to use. If the filter configuration changes, the whole component reactivating, so the tracking mechanism is restarting.

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

@Component(immediate = true, metatype = true)
@Properties(value = {
        @Property(name = "filterCriteria", value = "(objectClass=*)")
})
public class CustomTracker {

    private CustomServiceTracker customServiceTracker;

    @Activate
    protected void activate(ComponentContext componentContext) throws InvalidSyntaxException {
        String filterCriteria = (String) componentContext.getProperties().get("filterCriteria");
        customServiceTracker = new CustomServiceTracker(componentContext.getBundleContext(), filterCriteria);
        customServiceTracker.open(true);
    }

    @Deactivate
    protected void deactivate() {
        customServiceTracker.close();
    }

    /**
     * OSGi framework service tracker implementation. It is able to listen all serivces available in the system.
     */
    class CustomServiceTracker extends ServiceTracker {

        CustomServiceTracker(BundleContext bundleContext, String filterCriteria) throws InvalidSyntaxException {
            super(bundleContext, bundleContext.createFilter(filterCriteria), (ServiceTrackerCustomizer) null);
        }

        @SuppressWarnings("checkstyle:illegalcatch")
        @Override
        public Object addingService(ServiceReference serviceReference) {
            try {
                Object instance = super.addingService(serviceReference);
                // TODO: Whatever you need
                return instance;
            } catch (Exception e) {
                LOGGER.error("Error adding service", e);
            }
            return null;
        }

        @Override
        public void removedService(ServiceReference serviceReference, Object service) {
            // TODO: Whatever you need
            super.removedService(serviceReference, service);
        }

        @Override
        public void modifiedService(ServiceReference serviceReference,
                                    Object service) {
            super.modifiedService(serviceReference, service);
        }
    }
}

The only option I see for this use case is to use the OSGi API directly. It sounds like you have to do the service lookups each time you get an address to process. Thus you will have to get the appropriate service implementation (based on a filter) each time you are going to process an address.

Declarative approaches like DS and Blueprint will not enable you to do this, as the filters cannot be altered at runtime.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top