首页 > 解决方案 > Osgi ConfigurationAdmin 延迟激活组件

问题描述

我有一项需要配置的服务

@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 来检查配置是否已更新并且可以使用?

感谢尼尔,我能够找到一个可行的解决方案。在配置设置为等待服务后,我使用了 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 所做的出色工作。

标签: osgideclarative-services

解决方案


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

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

实际上,我们总是使用一种模式,当我们收到服务存在的通知时会做出反应。这可以通过多种方式完成,包括ServiceListenerand ServiceTracker,但最简单的是编写一个带有引用的组件,例如:

@Component
public class MyComponent {
    @Reference
    SampleService service;

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

这个组件有一个强制引用,并且只有当一个实例可用SampleService时才会被激活。SampleService


推荐阅读