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
доступен.