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.

1 ответ

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

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

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

@Component
public class MyComponent {
    @Reference
    SampleService service;

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

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

Другие вопросы по тегам