我正在开发一个可由客户扩展的应用程序。它基于OSGi(Equinox)并大量使用声明性服务(DS)。客户安装的bundle提供了他们自己的服务实现,然后我的应用程序可以使用它们。客户特定捆绑包可能提供的服务实施数量没有限制。
有没有办法确保在执行应用程序的主要功能时,所有客户提供的服务实现都已注册?
澄清一下,假设我的应用程序包含一个DS组件 RunnableRunner :
public class RunnableRunner
{
private final List<Runnable> runnables = new ArrayList<Runnable>();
public void bindRunnable(Runnable runnable)
{
runnables.add(runnable);
}
public void activate()
{
System.out.println("Running runnables:");
for (Runnable runnable : runnables) {
runnable.run();
}
System.out.println("Done running runnables.");
}
}
使用DS component.xml注册此组件,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="RunnableRunner" activate="activate">
<implementation class="RunnableRunner"/>
<reference bind="bindRunnable" interface="java.lang.Runnable" name="Runnable"
cardinality="0..n" policy="dynamic"/>
</scr:component>
我理解,无法保证在调用activate()
时,所有 Runnables都已绑定。事实上,我使用Eclipse / Equinox进行的实验表明,如果该捆绑包恰好在主捆绑包后启动,那么DS运行时将无法绑定由另一个捆绑包贡献的Runnables(这是50 /除非使用明确的起始等级,否则有50次机会。)
那么,我有哪些替代方案?在激活RunnableRunner之前,如何确保OSGi容器尽可能地尝试解析所有依赖项?
我已经考虑过的替代方案:
关于如何确保某些可扩展服务在使用前已“准备好”的任何建议?
答案 0 :(得分:2)
到目前为止,最好的方法是不关心和设计它正确流动的系统。服务出现和消失的原因有很多,所以任何稳定的幻想就是这样。不处理实际情况会造成脆弱的系统。
在您的示例中,为什么RunnableRunner
无法执行每个Runnable
服务的内容?以下代码完全是OSGi动态识别:
@Component
public class RunnableRunner {
@Reference Executor executor;
@Reference(policy=ReferencePolicy.DYNAMIC)
void addRunnable( Runnable r) {
executor.execute(r);
}
}
我希望你找到这个错误的原因是你没有指明。这是您应该尝试表达为服务注册的原因。
如果您有(罕见)用例,您绝对需要知道“全部”(无论这意味着)服务是否可用,那么您可以计算实例数,或使用其他条件。在使用DS的OSGi中,方法是将此条件转换为服务,以便其他人可以依赖它,并获得服务提供的所有保证。
在这种情况下,只需创建一个计算实例数的组件。使用配置,您可以在达到特定计数后注册Ready
服务。
public interface Ready {}
@Component
@Designate( Config.class )
public class RunnableGuard {
@ObjectClass
@interface Config {
int count();
}
int count = Integer.MAX_VALUE;
int current;
ServiceRegistration<Ready> registration;
@Activate
void activate(Config c, BundleContext context) {
this.context = context;
this.count = c.count();
count();
}
@Deactivate void deactivate() {
if ( registration != null )
registration.unregister();
}
@Reference
void addRunnable( Runnable r ) {
count(1);
}
void removeRunnable( Runnable r ) {
count(-1);
}
synchronized void count(int n) {
this.current += n;
if ( this.current >= count && registration==null)
registration = context.registerService(
Ready.class, new Ready() {}, null
);
if ( this.current < count && registration != null) {
registration.unregister();
registration = null;
}
}
}
您的RunnableRunner将如下所示:
@Component
public class RunnableRunner {
@Reference volatile List<Runnable> runnables;
@Reference Ready ready;
@Activate void activate() {
System.out.println("Running runnables:");
runnables.forEach( Runnable::run );
System.out.println("Done running runnables.");
}
}
相当脆弱的代码,但有时这是唯一的选择。
我不知道还有人在写XML ......我的心在流血: - )
答案 1 :(得分:0)
如果您不知道需要启动哪些扩展程序,那么您只能使组件动态化。然后,您可以在添加时对每个分机做出反应。
如果您需要确保在进一步的步骤之前收集了扩展程序,那么您可以使用所需扩展名称并在配置中命名。
所以例如你可以有一个配置属性&#34;扩展&#34;列出了空格所示的所有扩展名。然后,每个扩展必须具有类似&#34; name&#34;的服务属性。在组件中,然后按名称将找到的扩展名与所需的扩展名进行比较。然后你做了#34;激活&#34;只有当所有必需的扩展都存在时。
例如,这在CXF DOSGi中用于在远程服务管理规范中指定的服务上应用意图。