Задержка 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 используется внутри класса Main, подключенного к основному потоку osgi. Я использую 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.


person droopy    schedule 06.12.2018    source источник
comment
Можете объяснить, почему нужно ждать обновления конфигурации?   -  person Neil Bartlett    schedule 08.12.2018
comment
если я не буду ждать обновления конфигурации, я получу нулевую ссылку на InstrumenterServicecoverInstrumenter (потому что ConfigurationPolicy.REQUIRE) внутри класса SampleService. Если я подожду, сервис covergeInstrumenter больше не будет нулевым.   -  person droopy    schedule 10.12.2018
comment
Извините, я неправильно сформулировал свой вопрос. Почему вам нужно получить экземпляр службы тем же методом, которым вы задали конфигурацию?   -  person Neil Bartlett    schedule 10.12.2018
comment
Мне не обязательно это нужно в том же методе. Это упрощенный пример, отражающий проблему. В реальном приложении я не вызываю их из одного и того же метода, но у меня есть основной метод, привязанный к main.thread=true. В реальном приложении эта проблема возникает спорадически, так как между установкой конфигурации и созданием служб, которым требуется конфигурация, выполняется значительно больше операций.   -  person droopy    schedule 10.12.2018


Ответы (1)


Как поясняется в комментариях, этот код не совсем реалистичен. В производственном коде обычно не требуется обновлять запись конфигурации, а затем немедленно получать сервис, опубликованный компонентом. Это связано с тем, что любой такой код делает слишком много предположений о влиянии обновления конфигурации.

Вызов getServiceReference и getService возвращает только моментальный снимок состояния реестра службы в определенный момент. По своей сути ненадежно вызывать getService, ожидая, что он вернет значение.

На самом деле мы всегда используем шаблон, в котором реагируем на уведомление о существовании сервиса. Это можно сделать разными способами, включая ServiceListener и ServiceTracker, но проще всего написать компонент со ссылкой, например:

@Component
public class MyComponent {
    @Reference
    SampleService service;

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

Этот компонент имеет обязательную ссылку на SampleService и будет активирован только тогда, когда доступен экземпляр SampleService.

person Neil Bartlett    schedule 11.12.2018
comment
Я думаю, проблема в том, что основное приложение фактически запускается в одном основном потоке, который задает конфигурацию и справочные службы. Для большинства приложений я использовал DS (который мне очень нравится, кстати). Все службы разрешаются хорошо, кроме тех, которые используют ConfigurationAdmin. Я использую ConfigurationAdmin для установки свойства, которое затем автоматически активирует только правильный класс InstrumenterService (из многих, реализующих один и тот же интерфейс). Думаю, мне придется использовать шаблон, который реагирует на изменения ConfigurationAdmin. Спасибо за ваш ответ, и мне очень нравится osgi. - person droopy; 12.12.2018
comment
Спасибо за комментарий. Если вы не хотите выполнять рефакторинг для компонента DS, вы можете написать свой единственный метод, чтобы дождаться появления службы, например. используя ServiceTracker.waitForService(). Хотя, как я намекнул в своем ответе выше, я думаю, что это предполагает слишком много знаний о последующих эффектах внесения изменений в конфигурацию, что создает скрытую зависимость. - person Neil Bartlett; 12.12.2018