如何使用OSGi标准功能以编程方式在SCR中注册服务?

时间:2016-08-17 19:11:05

标签: java osgi

我花了一些时间试验和研究OSGi enRoute网站。 Quick StartBase教程非常好。现在作为一个学习练习,我正在按照这些教程中的原则创建我自己的例子。

我决定从博文“Making JavaFX better with OSGi”重现org.apache.felix.dm。我想使用OSGi标准SCR软件包(org.apache.felix.dm.annotation.api)以及enRoute提供程序模板,而不是使用org.osgi.service.component.*org.apache.felix.dm.DependencyManager软件包。

到目前为止,一切都很顺利。但我坚持一点。在“使用OSGi更好地制作JavaFX”教程中,使用 @Override public void start(Stage primaryStage) throws Exception { BundleContext bc = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); DependencyManager dm = new DependencyManager(bc); dm.add(dm.createComponent() .setInterface(StageService.class.getName(), null) .setImplementation(new StageServiceImpl(primaryStage))); } 以这种方式将服务以编程方式注册到服务注册表中:

DependencyManager

我的假设是,在此示例中,private void registerService(Stage stage) { DependencyManager dm = new DependencyManager(bundle().getBundleContext()); dm.add( dm.createComponent() .setInterface(StageService.class.getName(), null) .setImplementation(new StageServiceImpl(primaryStage)) ); } 是Apache Felix特定功能,而不是OSGi标准。我想让我的enRoute提供程序仅依赖于OSGi标准功能。

所以我的问题很简单:

  1. 如何仅使用OSGi标准功能以编程方式在服务注册表中注册服务? (我从enRoute教程中了解到,如果我的组件实现了导出的服务,那么当我的组件被激活时,SCR将自动在服务注册表中注册我的组件。但是这个解决方案的问题是当我的组件被激活时必须在不同的线程中启动JavaFX应用程序,以便在JavaFX应用程序终止之前不阻塞SCR使用的线程。因此,我的组件必须以编程方式在服务注册表中注册该服务。否则它将不会保证在注册时可用。
  2. 供参考,以下是我目前的情况:

    private void registerService(Stage stage) {
        // How to register service in service registry using only OSGi standard features? (not the apache felix dependency manager)
    }
    

    但我想用这个替换它:

    FrameworkUtil
      .getBundle(getClass())
      .getBundleContext()
      .registerService(StageService.class, new StageServiceImpl(primaryStage), null);
    

    更新1

    按照BJ Hargrave的建议,我尝试直接从捆绑上下文中注册服务,如下所示:

    The servicefactory:=true directive is set but no service is provided, ignoring it
    

    执行此操作并尝试解析enRoute应用程序项目后,会发生以下错误:

      

    org.osgi.service.resolver.ResolutionException:无法解析   <> version = null:缺少要求   com.github.axiopisty.osgi.javafx.launcher.application    - >无法解析com.github.axiopisty.osgi.javafx.launcher.application   version = 1.0.0.201608172037:缺少要求   的objectClass = com.github.axiopisty.osgi.javafx.launcher.api.StageService]

    我已将项目上传到github,以便您重现错误。

    更新2

    提供程序模块中bnd.bnd文件中的“构建”选项卡显示以下警告:

    {{1}}

    这可能与应用程序模块无法解决有关吗?

2 个答案:

答案 0 :(得分:3)

在极少数情况下,有必要使用标准OSGi API注册“手工服务”。尽量避免这种情况,因为如果您开始注册(并且可能依赖)您手动注册的服务,您将承担很多责任,这通常是从视图中隐藏的。例如,您必须确保您注册的服务也未注册。

在极少数情况下,必须等待条件才能注册您的服务。例如,您需要在为设备注册服务之前轮询一块硬件。您将需要控制CPU,但此时您还无法注册服务。在这种情况下,您将创建一个immediate组件并手动注册该服务。

要手动注册服务,您需要BundleContext个对象。您可以通过activate方法获取该对象,只需在其参数中声明一个Bundle Context并自动注入:

@Activate
void activate( BundleContext context) {
    this.context = context;
}

您现在可以使用捆绑上下文注册服务:

void register(MyService service) {
    Hashtable<String,Object> properties = new Hashtable<>();
    properties.put("foo", "bar");
    this.registration = context.registerService( MyService.class, service, properties );
}

但是,您现在有责任在停用时取消注册此服务。如果您不清理此服务,那么当您的服务仍然浮动时,您的组件可能会被停用。您的服务非托管。 (虽然当捆绑停止时,它将被清除。)

@Deactivate
void deactivate() {
   if ( this.registration != null)
     this.registration.unregister();
}

如果您创建的服务是回调或后台线程,那么您显然必须处理并发问题。您必须确保在停用方法完成时没有注册服务的竞争条件。

此文本也已添加到OSGi enRoute的DS Page

答案 1 :(得分:1)

阅读OSGi规范将有助于您了解服务API。

但是应该这样做:

ServiceRegistration<StageService> reg = bc.registerService(StageService.class, new StageServiceImpl(primaryStage), null);