As mentioned above, Spring-DM exporter and importer allow listeners to be used for receiving notifications on when services are bound, unbound, registered or unregistered. Below you can find some guidance advices when working with listeners:
java.util.Map
instead of java.util.Dictionary
class. The first is an interface while the latter is a deprecated, abstract class. To preserve compatibility, Spring-DM
will pass to the listeners a Map
implementation that can be casted, if needed, to a
Dictionary
.There are cases where an exporter/importer listener needs a reference back to the bean it is defined on:
<bean id="listener" class="cycle.Listener"> <property name="target" ref="importer" /> </bean> <osgi:reference id="service" interface="SomeService"> <osgi:listener bind-method="bind" ref="listener" /> </osgi:reference>
Listener bean | |
Dependency listener -> importer | |
Importer declaration | |
Dependency importer -> listener |
The declaration above while valid, creates a dependecy between the listener
and the importer it is defined upon.
In order to create the importer, the listener
has to be resolved and created but in order to do that,
the importer called service
needs to be retrieved (instantiated and configured). This cycle needs to be broken
down so that at least one bean can be fully created and configured. This scenario is supported is supported by Spring-DM
for both exporter and importers however, if the listener is defined as a nested bean, the cycle cannot be resolved:
<osgi:reference id="service" interface="SomeService"> <osgi:listener bind-method="bind"> <bean class="cycle.Listener"> <property name="target" ref="importer" /> </bean> </osgi:listener> </osgi:reference>
OSGi service importer | |
Dependency between importer -> listener | |
Nested listener declaration | |
Dependency nested listener -> importer |
The example above will fail since service
bean cannot be initialized as it depends on the
listener. The same cycle was seen before but in this case there is subtle yet big different from
the container perspective - the listener is declared as a nested/inner-bean (hence the missing bean id
).
Inner beans have the same life cycle as their declaring parents and do not have any name. By definition, they are not tracked
by the container and are simply created on demand. Since the importer cannot be partially created and the nested listener cannot
be cached, the container cannot break the cycle and create the beans. While the two configurations shown above seem similar, one works
while the other does not. Another reason to not use cycles unless you really, really have to.
To conclude, if you need inside the listener to hold a reference to the exporter/importer on which the listener is declared,
either declare the listener as a top-level bean (as shown before) or consider doing dependency lookup.
However, the latter approach requires extra contextual information such as the BeanFactory
to use and the bean
name and is more fragile then dependency injection.
For those interested in the technical details, neither the exporter and importer cannot be partially initialized since
they require the application context ClassLoader
which is requested through the
BeanClassLoaderAware
which relies on a buit-in BeanPostProcessor
which is applied only after the bean has been configured and is ready for initialization. If the ClassLoader
was not required then the exporter/importer could have been partially initialized and the case above supported.