I’m facing an issue in OSGi context with declarative services which I don’t understand. I try to explain:
I have a FooService
which needs the FooManagerService (1..1 static)
. The FooManagerService
references the FooService
, but it’s optional (0..n dynamic)
.
The goal is, if a FooService
becomes available, it registers (bind()
method is called) at the FooManagerService
, so that the FooManagerService
always has a list of all available FooService
implementations in the system.
It works well on Windows, but on Linux I encounter the problem, that the FooService
becomes active (activate()
method is called), but that isn’t recognized by the FooManagerService
(bind()
method isn’t called). If I disable and enable FooService
manually on the OSGi console, it is recognized by the FooManagerService
.
I don’t understand, why this happens. It can be avoided, by increasing the start level of the bundle, where FooServiceImpl
is located. But that feels like an ugly workaround for, that’s why I would like to understand what’s going on there.
I attach a picture which describes the references between the services. Any hint is appreciated. Thanks in advance!
Best regards
Steffi
Advertisement
Answer
There is a cycle here that should be ok according to the theory. However, there are a number of problems in practice.
First, your implementations should be immediate=true
. This solves some problems since it prevents a nasty problem that DS cannot get a service because it is being initialised. I.e. if the FooManager and the FooService impls must be immediate. This is described in OSGi enRoute Cycles
However, there is one more problem 🙁 Apache Felix DS has a bug that causes an effect as you describe. This bug is related to bundle ordering. This is reported in Apache Felix JIRA 5618.
If this DS bug is the problem then there is unfortunately only one solid solution. Unfortunate, because it requires you to descent to the bowels of OSGi. The solution is to register the manager service by hand and ensure it is not registered by DS:
@Component(service={}, immediate=true ) public class FooManagerImpl implements FooManager { private ServiceRegistration<FooManager> registration; @Reference volatile List<FooService> foos; @Activate void activate( BundleContext context, Map<String,Object> properties ) { registration = context.registerService(FooManager.class, this, new Hashtable<String,Object>(properties)); } @Deactivate void deactivate() { registration.unregister(); } ... }
The trick here is that the FooManager does not register its service until it has been activated while normally it is registered before it is activated.
I know Apache Felix is working on it but do not know how far they are.
Anyway, cycles always suck. Sadly, they are not always preventable but I would certainly try.
Note: registering a service manually will not create a capability. If you use Requirements/Capabilities you should add a service capability in the manifest to make the resolver work. If this line is gibberish to you, ignore it.