我有一个OSGi组件MyComponent
。
此组件引用了服务MyService
。现在MyService
有几个实现MyServiceImpl1
和MyServiceImpl2
。 MyComponent
也有财产MyProperty
。
现在我想要的是,只要MyProperty
为1,MyComponent.MyService
就会绑定到MyServiceImpl1
。如果我将MyProperty
更改为2,MyComponent.MyService
会动态更新MyService
绑定到“MyServiceImpl2”。
我如何实现这一目标?作为参考,我使用的是Apache Felix容器,并且更愿意避免使用较低级别的OSGi apis。
答案 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,...)。