2

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.

Dirk Fauth
  • 4,128
  • 2
  • 13
  • 23

1 Answers1

3

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.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • Can you please check (using the `scr:list` and `scr:info` commands) that the ConfigurableService is actually live and an instance exists. Also please get rid of the `unbind="-"` stuff, it's not needed and I don't know what side effects it might have. – Neil Bartlett Apr 29 '16 at 09:32
  • I removed the `unbind="-"` attribute. I just added it because the spec says it is needed to explicitly say that there is no unbind method. The new DS annotation support in Neon complains otherwise. For Felix I moved to Bndtools and there is no issue with a missing unbind method. Nevertheless it doesn't change anything. using `scr:list` and `scr:info` show that the `ConfigurableService` is active (`State: active`). For me everything looks fine. Is there anything special I should look for? – Dirk Fauth Apr 29 '16 at 10:34
  • The spec doesn't say that at all, so the DS annotation support in Neon needs to be fixed. You only need to say unbind="-" if there exists a method clashing with the inferred unbind method name. I.e. you have a method named `unsetConfigAdmin` but you do NOT want it to be used as the unbind method for `setConfigAdmin`. – Neil Bartlett Apr 29 '16 at 10:37
  • Regarding the remaining lifecycle issues... it's getting tricky to debug at this distance. Maybe if you put print statements in each of the bind/unbind/updated methods, and the activate/deactivate methods and then dump the output here? – Neil Bartlett Apr 29 '16 at 10:39
  • Sorry my fault, it is not the spec, it is the Javadoc of `@Reference` that says _To declare no unbind method, the value {@code "-"} must be used._. I added console outputs in each of the methods and also configured the ConfigureCommand to start immediatelly. I added the output in the original question for formatting reasons. – Dirk Fauth Apr 29 '16 at 10:55
  • Which version of the JavaDoc is that? The version I have is as follows... "To declare no unbind method **when the component type contains a method with the candidate name**, the value {@code "-"} must be used." (emphasis added). – Neil Bartlett Apr 29 '16 at 10:58
  • DS 1.2 I guess, as Equinox doesn't support 1.3 yet. – Dirk Fauth Apr 29 '16 at 11:00
  • Okay I see that in the R5 compendium. I guess the wording is not as clear as it should be which is why they updated it in R6. Regardless, it has *never* been necessary to specify unbind="-" unless you have a method name clash. So I think this should be raised as a bug against Neon. – Neil Bartlett Apr 29 '16 at 11:02
  • Thanks for the output. I'm suspicious whether you have a working Config Admin at all here. – Neil Bartlett Apr 29 '16 at 11:03
  • The Apache Felix Configuration Admin Service bundle is installed, resolved and active. And the ConfigurationCommand gets an instance injected and is able to operate on it. So I suspect it is there and working. Is there anything else I need to do? Or to check if it is really there? – Dirk Fauth Apr 29 '16 at 11:12
  • For the ConfigurationCommand (which is the only user of the ConfigAdmin directly) it says `SatisfiedReference: ConfigAdmin`. Is that what you asked for, or do you need another information? `scr:list`does not show the ConfigurationAdmin, but I suppose because it is not a DS component, correct? – Dirk Fauth Apr 29 '16 at 11:39
  • No I meant scr:info for ConfigurableService. Sorry I should have been more clear. Should we move this to chat? – Neil Bartlett Apr 29 '16 at 11:40
  • Sure, let's move to a chat and post the solution afterwards. BTW, I just noticed that you can configure whether the missing unbind method is reported as an Error or if it should be ignored. The default is Error, so there is no issue in Neon. Just a configuration that needs to be modified. – Dirk Fauth Apr 29 '16 at 11:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110637/discussion-between-dirk-fauth-and-neil-bartlett). – Dirk Fauth Apr 29 '16 at 11:54
  • Args, can't open a chat from my current location :( – Dirk Fauth Apr 29 '16 at 11:55
  • If the default in Neon is "error" then the default is wrong and this is a bug in Neon! – Neil Bartlett Apr 29 '16 at 12:41