【问题标题】:Osgi ConfigurationAdmin delay in activating ComponentOsgi ConfigurationAdmin 延迟激活组件
【发布时间】:2019-05-08 05:41:15
【问题描述】:

我有一个需要配置的服务

@Component(service=InstrumenterService.class ,configurationPid = "InstrumenterService", configurationPolicy = ConfigurationPolicy.REQUIRE, scope = ServiceScope.PROTOTYPE)
public class InstrumenterService

该服务在另一个服务中被引用:

@Component(service = SampleService.class, scope = ServiceScope.PROTOTYPE)
public class SampleService {

    @Reference(cardinality = ReferenceCardinality.OPTIONAL, scope = ReferenceScope.PROTOTYPE_REQUIRED, policyOption = ReferencePolicyOption.GREEDY)
    InstrumenterService coverageInstrumenter;

    public boolean hasInstrumenter() {
        if(coverageInstrumenter == null)
            return false;
        return true;
    }
}

此 SampleService 用于与主 osgi 线程挂钩的 Main 类中。 我正在使用 ComponentServiceObjects,因为我想按需创建 SampleServices。

@Component(immediate = true, property = "main.thread=true")
public class Main implements Runnable {

    @Reference
    ConfigurationAdmin cfgAdm;

    @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED)
    private ComponentServiceObjects<SampleService> sampleServices;

    public void run() {
        if (cfgAdm != null) {
            Configuration configuration;
            try {
                configuration = cfgAdm.getConfiguration("InstrumenterService", "?");
                Hashtable<String, Object> props = new Hashtable<>();
                props.put("some_prop", "some_value");
                configuration.update(props);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        SampleService servicess = sampleServices.getService();
        System.out.println(servicess.hasInstrumenter());
    }
}

我遇到的问题是 ConfigurationAdmin 设置的配置在 InstrumenterService 中不可见,除非我放一个 Thread.sleep(500);调用 configuration.update 后的命令。

我不太习惯使用 Thread.sleep 命令来确保配置更新可见。 是否有 API 可以检查配置是否已更新并可使用?

感谢 Neil,我能够找到一个可行的解决方案。 在配置设置为等待服务后,我使用了 ServiceTracker:

        BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
    ServiceTracker serviceTracker = new ServiceTracker(bundleContext, InstrumenterService.class.getName(), null);

    serviceTracker.open();
    try {
        serviceTracker.waitForService(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    serviceTracker.close();

我首先需要 ConfigurationAdmin 的原因是因为有一个接口 IInstrumenter 可以由许多不同的类实现。 该仪器的名称在 ConfigurationAdmin 中设置,然后在其他服务中进一步“自动”获取所需的仪器服务。

这样可以将任意数量的仪器添加到应用程序中,并且只需要知道仪器的名称才能使用它。

我还想提一下,通过 OSGI,我们设法将我们的单体遗留应用程序拆分为更多模块(约 15 个),它们不直接相互依赖,而是使用 API 层。

再次感谢您使用 OSGI 所做的出色工作。

【问题讨论】:

  • 您能解释一下为什么需要等待配置更新吗?
  • 如果我不等待配置更新,我将在 SampleService 类中获得 InstrumenterService coverageInstrumenter 的空引用(因为 ConfigurationPolicy.REQUIRE)。如果我等待,那么covergeInstrumenter 服务不再为空。
  • 抱歉,我的问题措辞有误。为什么需要在设置配置的相同方法中获取服务的实例?
  • 我不一定需要用同样的方法。这是一个捕获问题的简化示例。我是真正的应用程序,我不会从同一个方法调用它们,但我确实有一个 main 方法挂在 main.thread=true" 上。在实际应用程序中,这个问题偶尔会发生,因为在设置配置之间完成了更多的处理并创建需要配置的服务。

标签: osgi declarative-services


【解决方案1】:

正如 cmets 中所阐明的,此代码并不完全符合实际。在生产代码中,通常不需要更新配置记录,然后立即获取组件发布的服务。这是因为任何这样的代码都对配置更新的效果做出了太多的假设。

getServiceReferencegetService 的调用仅返回特定时刻服务注册表状态的快照。调用getService 期望它返回一个值本质上是不可靠的。

实际上,我们总是使用一种模式来响应服务存在的通知。这可以通过多种方式完成,包括ServiceListenerServiceTracker,但最简单的是编写一个带有引用的组件,例如:

@Component
public class MyComponent {
    @Reference
    SampleService service;

    public void doSomething() {
        println(service.hasInstrumenter());
    }
}

此组件强制引用SampleService,并且仅当SampleService 的实例可用时才会激活。

【讨论】:

  • 我认为问题在于主应用程序实际上是在一个设置配置和参考服务的主线程中启动的。对于大多数应用程序,我使用 DS(顺便说一句,我非常喜欢)。除了使用 ConfigurationAdmin 的服务外,所有服务都得到了很好的解决。我正在使用 ConfigurationAdmin 设置一个属性,然后该属性将仅自动激活正确的 InstrumenterService 类(来自许多实现相同接口的类)。我想我将不得不使用对 ConfigurationAdmin 更改做出反应的模式。谢谢你的回答,我非常喜欢osgi。
  • 感谢您的评论。如果您不想重构为 DS 组件,那么您可以编写单个方法来等待服务出现,例如使用ServiceTracker.waitForService()。尽管正如我在上面的回答中暗示的那样,我认为这假设了太多关于进行配置更改的下游影响的知识,这会产生隐藏的依赖关系。
猜你喜欢
  • 2014-01-12
  • 2013-07-09
  • 2019-08-31
  • 2016-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-04
相关资源
最近更新 更多