Service OSGi声明性服务在运行时筛选引用

Service OSGi声明性服务在运行时筛选引用,service,runtime,osgi,karaf,Service,Runtime,Osgi,Karaf,我在Karaf上尝试了一些OSGi声明性服务的例子(包括Blueprint)。我现在试图解决的问题是,如何在运行时获取对特定服务的引用(因此,在这里,注释和/或XML实际上不是一个选项) 我将解释我的用例: 我正试图设计一个系统来控制工业中的某些自动化过程(到目前为止只是在我的头脑中,这就是为什么我仍然只是在试验OSGi:)。为了与设备通信,使用了一组特殊的协议。为了使组件尽可能可重用,我设计了一个基于层的通信模型(例如用于网络的ISO/OSI模型,但要简单得多) 要将其转换为OSGi,我的系统

我在Karaf上尝试了一些OSGi声明性服务的例子(包括Blueprint)。我现在试图解决的问题是,如何在运行时获取对特定服务的引用(因此,在这里,注释和/或XML实际上不是一个选项)

我将解释我的用例:

我正试图设计一个系统来控制工业中的某些自动化过程(到目前为止只是在我的头脑中,这就是为什么我仍然只是在试验OSGi:)。为了与设备通信,使用了一组特殊的协议。为了使组件尽可能可重用,我设计了一个基于层的通信模型(例如用于网络的ISO/OSI模型,但要简单得多)

要将其转换为OSGi,我的系统的每一层都将由一组bundle组成。一个用于该层的接口,然后一个插件用于该层的每个实现(想象一下OSI传输层上的TCP与UDP)

要引用此类网络中的任何设备,将使用自定义地址格式(可提供两个此类地址的示例)xpa://12.5/03FE 或xpb://12.5/03FE). 该地址包含访问请求设备所需的层及其值的所有信息。您可以猜到,这个地址的每一部分都代表了我的网络模型的一层

这些地址将存储在一些配置数据库中(因此,同样,simple.cfg或.properties文件不是选项),以便可以在运行时远程更改它们

我正在考虑创建一个工厂,它将解析这个地址,并根据其所有组件创建一个对象链(从OSGi获取适当的服务),实现所有层并相应地配置它们

由于单个层可以有更多的实现(因此,实现单个接口的服务也会更多),因此该工厂需要在运行时(当它获得作为字符串传递的设备地址时)决定选择哪个特定的实现(根据服务将声明的附加属性)


如何在OSGi中实现这一点?对于这个用例,DS、Blueprint或其他什么方法更好?

我看到的唯一选择是直接使用OSGiAPI。听起来每次你得到一个要处理的地址时,你都必须进行服务查找。因此,每次处理地址时,都必须获得适当的服务实现(基于过滤器)


像DS和Blueprint这样的声明性方法不允许您这样做,因为过滤器在运行时无法更改。

我撤销了我的答案,因为接受的答案是正确的。当我回答这个问题时,我忽略了规范中这一点,但却是非常重要的细节。

OSGi提供了一种称为服务跟踪器的好方法。您可以在声明性服务中使用。在本例中,有一个配置,用于保存要使用的服务的筛选器。如果过滤器配置更改,整个组件将重新激活,因此跟踪机制将重新启动

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);
        }
    }
}

我意识到这是一个很晚才回答这个问题的答案,但这两个答案都忽略了对声明性服务中过滤的明显内置支持

可以使用@reference注释为DS引用定义目标筛选器:

@Component
public class ExampleComponent {
    @Reference(target="(foo=bar)")
    MyService myService;
}
还可以使用配置添加(或覆盖)此目标筛选器。对于组件:

@Component(configurationPid="fizz.buzz")
public class ExampleComponent {
    @Reference
    MyService myService;
}
pid
fizz.buzz
的配置字典可以使用键
myService.target
设置新的过滤器


这是一个比跳转到原始OSGiAPI好得多的选项,并且已经在多个规范版本中提供了它。

谢谢。我已经通过在运行时管理服务实例的ServiceRepository解决了这个问题。我的组件根据提供的地址字符串请求此存储库实现服务。如果该协议名称下不存在任何服务,则会引发相应的异常。存储库在服务实例注册/注销时动态管理它们。这样,一切仍然可以实现为声明式服务。在引擎盖下,实际组件属性是动态的;target=属性只是一个静态的方便助手。可以在运行时使用Configuration Admin更改声明性服务目标筛选器。这是规范的一部分,可以信赖。我不建议在这种情况下使用低级API。谢谢Tim!我们正是遇到了这个问题,在阅读了所有DS规范之后,我们没有变得更明智。任何地方都没有真正提到或解释目标过滤器属性是动态的这一事实(或者是吗?),可能会被误认为是DS运行时的实现细节。OSG R5概要第112.3.6节(选择目标服务):