我正在玩OSGi DS组件和ConfigurationAdmin。
我创建了一个简单的可配置组件
@Component(service=ConfigurableService.class)
public class ConfigurableService {
private String message;
@Activate
public void activate(Map<String, Object> params) {
System.out.println("Activate configurable");
message = (String) params.get("msg");
}
@Modified
public void modified(Map<String, Object> params) {
System.out.println("Modify configurable");
message = (String) params.get("msg");
}
@Deactivate
public void deactivate(Map<String, Object> params) {
System.out.println("Deactivate configurable");
message = (String) params.get("msg");
}
public void execute() {
System.out.println("Service says: " + message);
}
}
然后我创建了一个Felix Gogo shell命令组件来通过ConfigurationAdmin触发配置
@Component(property = {
CommandProcessor.COMMAND_SCOPE + "=fipro",
CommandProcessor.COMMAND_FUNCTION + "=configure"
},
service = ConfigurationCommand.class
)
public class ConfigurationCommand {
private ConfigurationAdmin cm;
@Reference(unbind="-")
public void setConfigAdmin(ConfigurationAdmin cm) {
this.cm = cm;
}
public void configure(String input) throws IOException {
Configuration config = cm.getConfiguration("org.fipro.osgi.config.ConfigurableService");
Hashtable<String, Object> props = new Hashtable<>();
props.put("msg", input);
config.update(props);
}
}
最后我创建了另一个利用ConfigurableService
@Component(property = {
CommandProcessor.COMMAND_SCOPE + "=fipro",
CommandProcessor.COMMAND_FUNCTION + "=welcome"
},
service = WelcomeCommand.class
)
public class WelcomeCommand {
private ConfigurableService service;
@Reference(unbind="-")
public void setConfigurable(ConfigurableService service) {
this.service = service;
}
public void updatedConfigurable(ConfigurableService service, Map<String, Object> properties) {
System.out.println("ConfigurableService updated");
}
public void welcome() {
service.execute();
}
}
如果我使用包含这些组件的bundle启动OSGi应用程序,我希望在最初执行welcome
时,我会看到该组件已激活且服务输出为null,因为尚未应用任何配置(确保连续通话时这会发生变化)。如果我之后执行configure Dirk
,我希望执行带有@Modified
注释的方法,以指示服务配置已更新。我还希望执行updatedConfigurable
中的WelcomeCommand
方法。至少这是我对阅读规范的理解。
现在我在Equinox和Felix中观察到不同的行为。
春分:
按预期调用已修改的方法,并正确配置ConfigurableService
。但updatedConfigurable(<Service>, <Map>)
未被调用。只有当我更改方法签名以获取ServiceReference
时,才会调用更新的方法。
规范说所有参考事件方法都支持以下方法签名
void <method-name>(ServiceReference);
void <method-name>(<parameter-type>);
void <method-name>(<parameter-type>, Map);
我在规范中没有看到更新方法是否有例外,或者这是Equinox中的一个问题,我应该为此提出一张票?
菲利克斯:
如果我在Bndtools中的Felix上运行相同的示例,则不会调用修改方法和更新方法。我检查了ConfigurationCommand
并且有一个ConfigurationAdmin可用,因此更新配置时没有例外。但它从未以某种方式应用过。
我在Felix上运行示例时遗漏了什么?
更新
向每个生命周期事件方法添加控制台输出会创建以下输出:
____________________________
Welcome to Apache Felix Gogo
g! ConfigurationCommand: Activate
ConfigurableService: Activate
WelcomeCommand: Activate
welcome
Service says: null
g! configure Dirk
g! welcome
Service says: null
g! exit 0
WelcomeCommand: Deactivate
ConfigurableService: Deactivate
ConfigurationCommand: Deactivate
如您所见,永远不会调用修改和更新的事件。
答案 0 :(得分:3)
我认为问题是Gogo命令的生命周期。
当命令未运行时,Gogo不会保留服务对象。它会跟踪ServiceReference
,但在您实际调用getService
命令之前不会调用welcome
。因此,当您调用welcome
时,WelcomeCommand
组件将被实例化,这会强制在那时实例化ConfigurableService
。
稍后当welcome
命令完成时,WelcomeCommand
被释放,因此WelcomeCommand
和ConfigurableService
都将被GC。因此,ConfigurableService
的任何实例都没有足够长的时间来接收Modified事件。
要解决此问题,请尝试立即WelcomeCommand
:
@Component(immediate = true, ...)
<强>更新强>
通过电子邮件与Dirk进一步讨论,结果是问题是位置绑定。在Config Admin中,配置默认情况下“绑定”到创建它们的包,在这种情况下是包含ConfigurationCommand
的包。一旦绑定,它们就不能被另一个bundle使用,因此ConfigurableService
永远不会看到配置。
要创建可供任何捆绑包使用的未绑定配置,请调用ConfigAdmin.getConfiguration()
的两个arg版本并为第二个arg传递null
。