1

Suppose I have the below OSGi component which is supposed to send an event every time a new implementation of SomeInterface is registered in the run-time.

For that I bind EventAdmin into eventAdmin variable and then use it inside bindSomeInterface method.

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

@Component
public class Sender {

    private EventAdmin eventAdmin;

    @Reference
    public void bindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

    public void unbindEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = null;
    }

    @Reference(cardinality = ReferenceCardinality.MULTIPLE)
    public void bindSomeInterface(SomeInterface instance) {

       // var prop create here... (non relevant code)
        Event event = new Event("topic", prop);

       // it is NULL!
        eventAdmin.sendEvent(event);
    }

    public void unbindSomeInterface(SomeInterface instance) {

    }

}

Generated xml file:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <implementation class="com.package.Sender"/>
</scr:component>

The PROBLEM

bindSomeInterface is being called first (getting a "notification" that a new instance of SomeInterface was registered in the run-time) and after that bindEventAdmin is called. This is not the desired effect.

Expected Behavior

I would like to get bind first the EventAdmin instance, and then the SomeInterface instances.

How can I do that?


Very close question (BUT not the same): Bind order of OSGi declarative services


PS: I'm trying to avoid ServiceTrakers and that sort of stuff.

lealceldeiro
  • 14,342
  • 6
  • 49
  • 80

3 Answers3

3

While you can control the order of injection using either lexical ordering (as suggested by Peter) or by manually ordering the XML elements, I would recommend against relying on this.

In your example you want to send an event to EventAdmin when both the EventAdmin and SomeInterface services are bound. The perfect place to do this, along with any other required initialization, is in the activate method of the component. The activate method is guaranteed to be invoked after all static references are bound. In your case the cardinality of SomeInterface is 0..n so it may be called zero to many times. You can accumulate all the instances into a List and iterate that list from the activate method.

You don't even need to worry about making the List thread-safe or using synchronization, because SCR makes sure there is a "happens-before" relationship between the last service binding and the start of the activate method.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
2

If you are using bnd to generate the XML then the order is based on the lexical order of the reference name. I.e. the references are sorted by name before written to the XML.

However, the second reference is dynamic. I am not absolutely sure you can rely then on the order. I could envision that if a new service comes in just before SCR tries to inject the Event Admin that is calling bindSomeInterface before. 1

That said, this is all into great detail outlined in the specification of the Declarative Services.

1 In the original annotations that were developed in bnd before OSGi standardized them I made the default for MULTIPLE DYNAMIC because it seems very dangerous to not be dynamic when you have multiple. During the standardization process, I seem not to have been able to convince the other members that the MULTIPLE STATIC is a really bad combination because the result is not deterministic and can change per startup.

Peter Kriens
  • 15,196
  • 1
  • 37
  • 55
  • 1
    You're correct, the reference you have specified is static because that is the default, and changing the cardinality does not affect the policy. – Neil Bartlett Apr 19 '19 at 00:54
0

Simply changing the tags order does the "trick". The xml file now looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.package.Sender">
   <reference bind="bindEventAdmin" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static" unbind="unbindEventAdmin"/>
   <reference bind="bindSomeInterface" cardinality="0..n" interface="com.package.bindSomeInterface" name="SomeInterface" policy="static" unbind="unbindSomeInterface"/>
   <implementation class="com.package.Sender"/>
</scr:component>

Obviously the framework tries to resolve all the references in the same order they are declared in the xml file.

Which is interesting is when you implement your class using declarative services and don't look too much at the xml file generated by the IDE (Eclipse in this case) this might be a nightmare as you expect the services to be resolved in the same order (sequence of lines of code in the .java file) you declared the bind and unbind methods.

lealceldeiro
  • 14,342
  • 6
  • 49
  • 80
  • 1
    Yes this could be a nightmare... so don't rely on service injection order! Do your initialisation in the activate method. See my separate answer on this. – Neil Bartlett Apr 19 '19 at 00:54