Reputation: 4221
I'm playing around with OSGi DS components and the ConfigurationAdmin.
I created a simple configurable component
@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);
}
}
Then I created a Felix Gogo shell command component to trigger the configuration via 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);
}
}
And at last I created another Felix Gogo shell command component that makes use of the 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();
}
}
If I start an OSGi application with the bundles that contain these components, I expect that on executing welcome
initially, I will see that the component is activated and the service output is null because no configuration is applied yet (sure this changes on consecutive calls). If I afterwards execute configure Dirk
I expect that the method annotated with @Modified
is executed to indicate that the service configuration has been updated. I also expect that the updatedConfigurable
method in the WelcomeCommand
is executed. At least that is my understanding from reading the spec.
Now I observe different behavior in Equinox and Felix.
Equinox:
The modified method is called as expected and the ConfigurableService
is configured correctly. But the updatedConfigurable(<Service>, <Map>)
is not called. Only if I change the method signature to take a ServiceReference
the updated method gets called.
The specification says that all reference event methods support the following method signatures
void <method-name>(ServiceReference);
void <method-name>(<parameter-type>);
void <method-name>(<parameter-type>, Map);
Is there an exception for the updated method I haven't seen in the spec or is this an issue in Equinox where I should raise a ticket for?
Felix:
If I run the same example on Felix in Bndtools, neither the modified nor the update methods get called. I checked the ConfigurationCommand
and there is a ConfigurationAdmin available so there is no exception on updating the configuration. But it gets never applied somehow.
Am I missing something on running the example on Felix?
Update:
Adding console outputs to every lifecycle event method creates the following output:
____________________________
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
As you can see, the modify and updated events are never called.
Upvotes: 3
Views: 1804
Reputation: 23948
I think the problem is the lifecycle for Gogo commands.
Gogo does not hold on to service objects while a command is not running. It tracks the ServiceReference
but does not call getService
until you actually invoke the welcome
command. Therefore when you invoke welcome
, the WelcomeCommand
component will be instantiated, which forces the instantiation of the ConfigurableService
at that time.
Later when the welcome
command completes, the WelcomeCommand
is released, therefore both WelcomeCommand
and ConfigurableService
will be GC'd. Therefore no instance of ConfigurableService
ever lives long enough to receive the Modified event.
To address this, try making WelcomeCommand
immediate:
@Component(immediate = true, ...)
UPDATE
On further discussion with Dirk over email, it turns out that the issue is location binding. In Config Admin, configurations are by default "bound" to the bundle which creates them, in this case the bundle that contains ConfigurationCommand
. Once bound they cannot be consumed by another bundle, so the ConfigurableService
never sees the config.
To create an unbound configuration that can be used by any bundle, call the two-arg version of ConfigAdmin.getConfiguration()
and pass null
for the second arg.
Upvotes: 4