【问题标题】:ServiceTracker doesn't find an existing serviceServiceTracker 找不到现有服务
【发布时间】:2011-07-19 06:31:09
【问题描述】:

我正在使用 ServiceTracker 来定位我们 OSGi 环境中的注册服务。 我在 Bundle Activator 启动方法中得到了这段代码:

    logger.debug("looking for MyService");
    tracker = new ServiceTracker(ctx, MyService.class.getName(), null);
    tracker.open();
    MyService = ((MyService)tracker.getService());
    if (MyService != null)
    {
        logger.debug("found MyService");
    }

问题是这样的:

  • 如果我安装并启动我的捆绑包 可以找到并使用服务。
  • 如果我完全重启 OSGi MyService 不能 由我的捆绑包找到(即为 NULL),即使 我的捆绑包处于 ACTIVE 状态。
  • 如果我停止/启动我的捆绑包 MyService 可以找到并再次使用。

我认为问题不在于托管 MyService 的捆绑包,因为它显然在那里,并且如果我的捆绑包重新启动,可以再次找到它。

看起来我的包在包含依赖服务的包之前加载,这就是为什么它在重新启动后找不到它,而在我重新启动我的包后可以找到它。

这表明如果我使用

列出可用服务
ServiceReference[] ref = tracker.getServiceReferences();

在 OSGi 重新启动后它没有找到任何服务,但在我停止/启动我的包后它确实找到了 MyService。

我试图将 Require-Bundle 引用设置为托管 MyService 的包,希望 OSGi 框架能够识别依赖关系,但它没有帮助。

有什么想法吗...?

【问题讨论】:

    标签: osgi


    【解决方案1】:

    OSGi 中的服务非常不稳定,因此您永远不应该期望在您需要它时会出现,或者在获得它后就留在那儿。这就是为什么你的 bundle 应该让自己被异步通知服务。

    ServiceTracker 类接受 ServiceTrackerCustomizer(它本身就是一个),当服务来来去去时会收到通知。

    大多数时候,使用服务跟踪器的正确方法如下:

    // In BundleActivator.start:
    this.serviceTracker = new ServiceTracker(bundleContext, MyService.class.getName(), null) {
        public Object addingService(ServiceReference reference) {
            // Get the service
            final MyService service = (MyService)this.context.getService(reference);
    
            // Do something with the service (e.g. inject it somewhere)
            // ...
    
            // Return the service to track it
            return service;
        }
    
        public void removedService(ServiceReference reference, Object service) {
            // Stop using the service (e.g. notify the objects that use it)
            // ...
    
            // Unget the service (very important!)
            this.context.unget(reference);
        }
    }
    

    请注意,我们只跟踪MyService 服务并且不使用任何定制器(我们将null 作为第三个参数传递给构造函数),而是覆盖了两个重要的方法。另一个重要的方法是modifiedService;请阅读 Javadoc 了解更多信息。

    管理这很快就会成为一个严重的负担,因此您应该考虑使用更高级别的抽象,如声明式服务(正如另一个答案所建议的那样)。

    【讨论】:

      【解决方案2】:

      您正在使用 ServiceTracker 做正确的事。但是问题是期望在激活器中可以使用作为跟踪器的服务。您不想在启动包和注册服务的包之间建立排序约束。除非您确实需要在激活器的启动方法中使用该服务(并且您可能不应该),否则请稍后在您真正需要时获取该服务。

      另一个想法是考虑使用声明式服务来管理您的服务依赖项。

      【讨论】:

      • 我正在尝试在 OSGI 中部署两个捆绑包,但只有一个会激活,无论哪个先部署。我无法从第二个捆绑包访问服务。有什么解决办法吗?
      【解决方案3】:

      如果您使用BundleActivator 中的ServiceTracker,您将有效地冻结整个框架(因此,不能同时启动其他包)。如果提供服务的包与跟踪器的包之后启动,您将看不到该服务。这解释了为什么稍后停止和启动您的捆绑软件可以为您提供服务。

      现在,如果您想跟踪和使用该服务,我会创建一个新线程来执行此操作,并使用 waitForService 代替 getService

      【讨论】:

      • 谢谢,这可能解释了原因。 ServiceTracket 建议不要在 start 方法中使用 waitForService() (“强烈建议在调用 BundleActivator 方法的过程中不要使用 waitForService。BundleActivator 方法预计会在短时间内完成。”)。在这种情况下,获得对服务的引用的更好方法是什么?也许在 Activator 类中保存对 bundlecontext 的引用,然后从 bundle 代码本身调用 ServiceTracker.getService()?
      • 如果您需要从您的捆绑包中访问BundleContext,您确实应该以某种方式从Activator 中保存它,然后使用它来获取您的服务。我还鼓励您查看一些与此相关的系统,例如 OSGi 规范中的声明式服务、iPojo 或 Apache Felix 依赖管理器。
      • 我尝试了 waitForService,现在包在 STARTING 上挂起,并且挂起整个引导程序,所以我认为 waitForService 不是一个好主意...
      • 我们使用 Equinox 作为框架,使用 Guice/Peaberry 作为 DI,但是 Guice/Peaberry 已经完成了这项服务的绑定,我现在只需要作为客户端使用它。
      • 如果你不在新线程中使用 waitForService,它肯定会挂起。如果您已经有了 DI 解决方案,为什么不从客户端捆绑包中使用它?
      猜你喜欢
      • 1970-01-01
      • 2014-11-17
      • 1970-01-01
      • 1970-01-01
      • 2012-09-28
      • 1970-01-01
      • 2016-12-17
      • 1970-01-01
      相关资源
      最近更新 更多