如何动态更新OSGi组件中的引用?

时间:2016-06-21 09:06:16

标签: osgi apache-felix

我有一个OSGi组件MyComponent

此组件引用了服务MyService。现在MyService有几个实现MyServiceImpl1MyServiceImpl2MyComponent也有财产MyProperty

现在我想要的是,只要MyProperty为1,MyComponent.MyService就会绑定到MyServiceImpl1。如果我将MyProperty更改为2,MyComponent.MyService会动态更新MyService绑定到“MyServiceImpl2”。

我如何实现这一目标?作为参考,我使用的是Apache Felix容器,并且更愿意避免使用较低级别的OSGi apis。

3 个答案:

答案 0 :(得分:7)

配置依赖项的最简单方法是使用'.target'属性。这要求实现注册一个标识属性,比如说impl=1。和impl=2

@Component(property="impl=1")
public class MyServiceImpl1 implements MyService {
}
@Component(property="impl=2")
public class MyServiceImpl2 implements MyService {
}

然后该组件可能如下所示:

@Component
public class MyComponent {
    @Reference(target="(impl=1)")
    volatile MyService myService;
}

在这种情况下,您将无法使用1或2作为标志,但您必须使用另一个过滤器修改名为MyComponent的{​​{1}}的配置属性。 (这与OSGi标准化注释一起显示。)

如果你坚持使用{1}}的1或2属性(我们称之为myService.target),那就更精细了。首先,我们有满足的问题。 MyComponent是否应该根据select属性impl 1满足,但只有2可用?如果可以,那么以下更加复杂的解决方案应该可行

select

正如您所看到的,组件开始处理自己的依赖项通常是一个坏主意。因此,我对你的现实世界情景感到好奇。

我发现总是非常尴尬和难以维护设计,其中组件对于他们引用的人是挑剔的。它有时是不可避免的,但在MyComponent@Designate( ocd=Config.class ) @Component( property = "select=1" ) public class MyComponent { static Class<?> types [] = { MyServiceImpl1.class, MyServiceImpl2.class, }; @interface Config { int select() default 1; } @Reference(target="(|(impl=1)(impl=2))") volatile List<MyService> candidates; volatile MyService selected; @Activate @Modify void changed( Config config ) { Class<?> type = types[config.select()]; Optional<MyService> service = candidates. stream(). filter( type::isInstance ). findFirst(); this.service = service.isPresent() ? service.get() : null; } } 决定注册或不基于某些条件的一般解决方案中,更好地反映现实。

因此,我对1或2属性在现实世界中反映出来的重大问题是什么?这不能被建模为服务依赖吗?

(免责声明:代码未经过测试且未处理错误)

答案 1 :(得分:2)

我假设可以查询MyService的实施情况以报告其类型(例如下面):

public interface MyService {
    public static final String TYPE = "myservice.type";
}

如果是这样,对于Apache Felix上的声明性服务OSGi组件,这是一种方法:

  • MyService中保留MyComponent引用
    • Dynamic参考政策(policy = ReferencePolicy.DYNAMIC
    • 1..n基数(cardinality = ReferenceCardinality.MANDATORY_MULTIPLE
  • bind/unbind
  • 中安装MyService引用的MyComponent方法
  • Modified
  • 中安装MyComponent方法
bind/unbind实现由Felix SCR实例化时,MyComponent

MyService方法将被调用。您可能希望维护可用实现的映射。

只要有Modified的配置更新事件,就会调用

MyComponent方法。在此方法中,基于更新的组件配置属性,可以选择适当的方法进行进一步处理。

这是使用Felix SCR annotations时组件的外观。

@Component (metatype = true, immediate = true)
@Service (value = MyComponent.class)
public class MyComponent {
    @Property(name = "impl.selector", value = "impl_1")
    private String implSelector = "impl_1";

    @Reference(
        referenceInterface = MyService.class,
        policy = ReferencePolicy.DYNAMIC,
        cardinality = ReferenceCardinality.MANDATORY_MULTIPLE,
        strategy = ReferenceStrategy.EVENT,
        bind = "bindService",
        unbind = "unbindService"
    )

    private Map<String, MyService> availableMyServiceImpls = new HashMap<String, MyService>();
    private MyService service = null;

    @Activate
    public void activate(ComponentContext componentContext) {
        service = availableMyServiceImpls.get(implSelector);
    }

    @Deactivate
    public void deactivate(ComponentContext componentContext) {
        availableMyServiceImpls.clear();
    }

    public void bindService(MyService serviceRef, Map<?,?> refProperties) {
        String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY);
        availableMyServiceImpls.put(serviceImplName, serviceRef);
    }

    public void unbindService(MyService serviceRef, Map<?,?> refProperties) {
        String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY);
        availableMyServiceImpls.remove(serviceImplName);
    }

    @Modified
    public void modified(ComponentContext componentContext) {
        Dictionary<String, Object> componentProps = componentContext.getProperties();
        implSelector = PropertiesUtil.toString(componentProps.get("impl.selector"), "");
        service = availableMyServiceImpls.get(implSelector);
    }
}

答案 2 :(得分:1)

首先,您必须使用不同的过滤器发布您的实现,以便能够通过过滤获得一个或其他。 然后,只要使用bundleContext.getServiceReferences()更改属性,就可以更改ServiceReference。

技术细节取决于您使用的框架(DS,iPojo,none,...)。