Search code examples
javalinuxosgiequinoxdeclarative-services

OSGi declarative service is active, but bind() is not called


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

Service Manager Diagram


Solution

  • 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.