Defining the start order of OSGi services

I have two services declared with @Component. I need to make sure that the servlets starts before the HandlerFactory service, because initialization depends on receiving a pairing code, which is handled by the servlet.

@Component(service = { ThingHandlerFactory.class,
        MagentaTVHandlerFactory.class }, immediate = true, configurationPid = "binding." + BINDING_ID)
public class MagentaTVHandlerFactory extends BaseThingHandlerFactory {

and

@Component(service = HttpServlet.class, configurationPolicy = ConfigurationPolicy.OPTIONAL, immediate = true)

I looked up some OSGi tutorials and saw that there is a way to set the level during installation, but I’m looking for a way to do this with the @Component declaration. I think that’s not an unusual requirement for depended services.

Any idea?

You can force “logical” dependency on services by injecting your servlet into handler factory. I know it sounds strange, however its easiest way to slow down handler factory.

Be aware that registration of HttpServlet as an OSGi service doesn’t really mean that it will be available.

@markus7017 Are you using http whiteboard or registering servlet manually in http service?

Hi, what do you means with “injection”? how to achieve that?

I found a solution using a @Reference to bind the factory to the servlet. At that moment the servlet is started and calls the factory to set a flag. This flag is used to delay the asynchronous thing initialization. At the end the thing initialization waits until the servrlet signals “initialized”. Ok, it works, but I would expect a smarter solution, because the concept of start levels is well know in the OSGi spec. I couldn’t find a way to add the level to the @Component or something like shat and couldn’t find an example (e.g. other bindings, google).

I use the activate() mechanism to register the url

    @Activate
    protected void activate(Map<String, Object> config) {
        try {
            httpService.registerServlet(PAIRING_NOTIFY_URI, this, null, httpService.createDefaultHttpContext());
            logger.info("Servlet started at '{}'", PAIRING_NOTIFY_URI);
        } catch (ServletException | NamespaceException e) {
            logger.error("Could not start: {} ({})", e.getMessage(), e.getClass());
        }
    }

and learned that at that point the handler factory is not bound. So I used the bind call to signal the status to the handler factory.

    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
    public void setMagentaTVHandlerFactory(MagentaTVHandlerFactory handlerFactory) {
        logger.debug("HandlerFactory bound to NotifyServlet");
        this.handlerFactory = handlerFactory;
        handlerFactory.setNotifyServletStatus(true);
    }

The thing initialization is started asynchronous

@Override
    public void initialize() {

        // The framework requires you to return from this method quickly. For that the initialization itself is executed
        // asynchronously
        logger.debug("Initialize Thing...");
        updateStatus(ThingStatus.UNKNOWN);
        scheduler.execute(() -> {
            String errorMessage = "";
            try {
...

and then I wait on the initialization signal from the servlet.

                    // wait for NotifyServlet to initialze
                    if (!handlerFactory.getNotifyServletStatus()) {
                        logger.debug("Waiting on NotifyServlet to start...");
                        int iRetries = 30;
                        while ((iRetries-- > 0) && !handlerFactory.getNotifyServletStatus()) {
                            logger.trace("Waiting for init, {} sec remaining", iRetries);
                            Thread.sleep(1000);
                        }
                        if ((iRetries <= 0) && !handlerFactory.getNotifyServletStatus()) {
                            errorMessage = "Can't initialize, NotifyServlet not started!";
                        }
                    } else {
                        connectReceiver(); // throws exception on error
                    }

As said: works, but feels like a hack for something, which is supported by the OSGi spec.

If you want to look at the details, this is the repo:

That’s indeed a dependency injection I meant in my first reply. Sorry for not being clear - you did it in simplest way which will work.
I took a look on your code and found there is bidirectional dependency between MagentaTVHandlerFactory and MagentaTVNotifyServlet - meaning that servlet is injected to factory and vice versa. This is a cyclic dependency.

Now, to address situation you described that HandlerFactory waits for Servlet initialization completion. To solve that in proper way you can register new service from notify servlet once its bootstrapping is done. You can do that via:
val reg = bundleContext.registerService(MagentaTVNotificationService.class, new Hashtable(), new MemoryMagentaTVNotificationService());

When servlet shut down or went back to uninitialized state you need to call reg.unregister(); to signal shutdown of your service.

I know, its all pseudo code, however intention is fairly simple - break direct and cyclic dependency between services into gentle one with service which is needed.

Some more pseudocode for you:

final class Notification {
        public final String device, String ip, String code;
        Notification(device, ip, code) {
            this.device = device;
            this.ip = ip;
            this.code = code
        }
    }
}

// contract for broadcasting received pairing code
interface MagentaTVPairingListener {
    void receiveCode(Notification notification);
}

// service which broadcast notifications
interface MagentaTVNotificationService {
   void registerListener(MagentaTVPairingListener listener);
   void notify(Notification notification);
}

class MemoryMagentaTVNotificationService implements MagentaTVNotificationService {
    private final List<MagentaTVPairingListener> listeners = new CopyOnWriteArrayList();

    void registerListener(MagentaTVPairingListener listener) {
        listeners.add(listener);
        if (notification != null) {
            listener.notify(notification);
        }
    }

    void notify(String device, String ip, String code) {
        // you might want to synchronize this block to avoid eventual double calls on listener
        this.notification = new Notification(device, ip, code);
        listeners.forEach(listener -> listener.notify(this.notification));
    }
} 

From design point of view whole thing looks like this:


Servlet --(register)--> NotificationService <--(watch)-- Handler
    |                            |       |  `--(register)-- PairingListener       
    |(receive code)              |       |
    `---------(broadcast to)-----'       `--(notify)--> PairingListener

I’m not sure if its not over complicated for your case, however it does separate responsibilities. Service registration and watching is a common pattern which is used under OSGi and builds up great success of platform. For example by making a mandatory @Reference you force your component to wait until desired service is present and also shut it down when referenced service is gone.
With your approach you managed to achieve first but missed (I guess) second step.

As a note - a mentioned whiteboard pattern would rely on registration of MagentaTVPairingListener as services. In your case that’s not what you need as you need to ensure that elements who are interested in receiving of notifications are not launched before element which broadcasts them.

Hope that above will let you solve your design quiz.

Cheers,
Łukasz

Hi Łukasz,
thanks for your feedback. So if I understand correctly you create an instance it between - one side registering a listener the other side doing the notification and having the notification service in between. If the client starts before the listener (factory) is up the service buffers the and call the notification hander once the listener registers. That’s a vlid approach to decouple both side, but prevent the dependencies in both directions.

Nevertheless there bust be a way that the frameworks handles this “startup” problem, because it defines the concept of start levels so I would expect that there is a declarative approach without any code.

I’ll think about the above solution, because the factory also acts as a dispatcher for incoming events and you approach is more elegant rather than managing a table of devices (representing the thing / thing handler).

The Spotify binding implements a similar pattern. It only doesn’t use a NotificationService inbetween but directly binds servlet with the bridge(handler).

In general I don’t think this is a problem of the framework. Because the framework starting should not be dependent on external (incoming) calls. It just starts the services. The services than start in scheduled tasks that call the external services or wait for external services. And thus if several parts of a binding depends on specific state it should be designed such that is can handle it. And that is probably not something that is specific to startup, but should be able to handle such state changes at any point in time. For example if the external device is off line at startup of the framework than it would simply mean the binding would handle it as off line. If the external device would come online than the binding should still be able to handle it, and at that point it has nothing to do with startup order anymore.

Start levels are intended to sort in which sequence you want to launch bundles but this does not imply a guarantee of startup order for services. Usually there is more than just one thread which is used to bootstrap bundles. Even if startup order would be a acknowledged solution in your case it doesn’t help as your services are all registered from same bundle. See OSGi has no start ordering, lack of it is intentional.

OSGi way is simple - your services might come and go and your code should be ready and aware of that. If your thing handler factory requires notifications from somewhere else, express that via service relationships.

@hilbrand Above introduction of *Notification types was intended by ease of testing. You don’t need to initialize servlet and can easily mock up either listener or notification service itself. Works with basic junit and requires no framework tests. :slight_smile:

ok, this was my understanding. For me it’s strange that there is no way to define dependencies between services, but it is what it is :slight_smile:

That sounds like a easy change: Adding a @Reference to the handler factory. If the setXXX handler is called the factory knows that the servlet has started and the unsetXXX call indicates that the servlet was unloaded (we should never happen). This saves the explicit sync bzw. factory and servlet, which makes it more robust. So the initialization will be delayed until the servlet bound to the factory (setXXX was called).

Would that work?

    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
    public void setMagentaTVNotifyServlet(MagentaTVNotifyServlet notifyServlet) {
        if (notifyServletInitialized == false) {
            notifyServletInitialized = true;
            logger.debug("NotifyServlet bound to HandlerFactory");
        } else {
            logger.error("NotifyServlet was restarted!");
        }
    }

    public void unsetMagentaTVNotifyServlet(MagentaTVNotifyServlet notifyServlet) {
        if (notifyServletInitialized == true) {
            logger.debug("NotifyServlet was restarted");
            notifyServletInitialized = false;
        } else {
            logger.debug("NotifyServlet was unbound from HandlerFactory!");
        }
    }