OSGi:确保在Declarative Services应用程序中加载所有扩展

时间:2016-08-10 14:00:07

标签: java eclipse osgi declarative-services

我正在开发一个可由客户扩展的应用程序。它基于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容器尽可能地尝试解析所有依赖项?

我已经考虑过的替代方案:

  • 捆绑启动级别:太粗糙(它们在捆绑级别上工作,而不是在组件级别上工作)并且也不可靠(它们仅作为OSGi的提示)
  • 求助于Eclipse的扩展点:特定于Eclipse,很难与Declarative Services结合使用。
  • 每当注册一个新的Runnable时,使RunnableRunner动态重新配置:不可能,在某些时候我必须按顺序执行所有Runnables。

关于如何确保某些可扩展服务在使用前已“准备好”的任何建议?

2 个答案:

答案 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中用于在远程服务管理规范中指定的服务上应用意图。