2

I have such issue. I have two OSGI blueprint bundles. One of them is like a service and another is using it. I'm running them on karaf. So, I want to implement functionality so when I'm stopping service then my other bundle also should be stopped. My xml's

    <?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd ">

    <reference id="weatherService" availability="mandatory" interface="com.myslyv4uk.weather.api.WeatherService" />  

    <bean id="showWeatherImpl" class="com.myslyv4uk.client.impl.ShowWeatherServiceImpl"
        init-method="start" destroy-method="stop" >
        <argument ref="weatherService" />
    </bean>

</blueprint>

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd ">

    <bean id="weatherServiceImpl" class="com.myslyv4uk.weather.impl.WeatherServiceImpl"
        init-method="start" destroy-method="stop" />

    <service ref="weatherServiceImpl">
        <interfaces>
            <value>com.myslyv4uk.weather.api.WeatherService</value>
        </interfaces>
    </service>  
</blueprint>

Java code skipped. I will just say that ShowWeatherService uses WeatherService to print random number. They both have start/stop method. I need to implement configuration or functionality in such way so after uninstall WeatherService bundle from karaf also ShowWeatherService was stopped. The problem is that I cannot do reference from WeatherService to ShowWeatherService because it will be cyclic reference it this bundles won't start up. What should I do? How could I terminate bundle from other bundle?

Bohdan Myslyvchuk
  • 1,657
  • 3
  • 24
  • 39
  • 1
    Check [OSGi SCR](http://felix.apache.org/documentation/subprojects/apache-felix-service-component-runtime.html). You can have "components" that are activated/deactivated based on presence/absence of OSGi service (among others) – Grzegorz Grzybek Oct 13 '17 at 10:48
  • @GrzegorzGrzybek thank for your comment, but as I understood it's declarative services. And I'm using blueprint. Rewriting into declarative services is a last resort which I want to avoid – Bohdan Myslyvchuk Oct 13 '17 at 10:52

2 Answers2

2

I would not stop a bundle that needs a service when this service goes down. This is not the way this is meant to be handled in OSGi.

Instead your showWeatherImpl bundle could offer itself as an servlet using the http whiteboard pattern. This means it offers a service that implements the servlet. Blueprint will automatically unregister all services of a bundle if a mandatory service reference goes down.

Of course this does not help if you for example register yourself as a servlet using java code in a bean in showWeatherImpl. In this case you can use service reference callbacks that will notify you when services come and go.

And of course like Grzegorz mentioned declarative services is much more dynamic than blueprint by default and handles such situations a lot better.

Christian Schneider
  • 19,420
  • 2
  • 39
  • 64
  • AFAIK, if a mandatory service reference goes down any subsequent call on that instance will block (since you never get a service, but a blueprint proxy to a service) and eventually raise `ServiceUnavailableException`. – Alessandro Da Rugna Oct 16 '17 at 07:11
  • Yes. That is true. So this only works if you have an unbroken chain of services. The OSGi http whiteboard is such a case. In other cases once you get a call from outside into your bean while its dependency is not available it blocks. For me this is one of the reasons why I try to only use DS if I can. – Christian Schneider Oct 17 '17 at 14:38
1

WARNING

In this answer I explain how to stop a Blueprint bundle when a required service disappears. This code works as intended, however it is a bad idea for various reasons.


You can register a listener on bind/unbind of a service, and act upon the removal of the service. This is an example on how to do it.

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
    xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd ">

    <bean id="referenceListener" class="your.ReferenceListener">
        <property name="bundleContext" ref="blueprintBundleContext"/>
    </bean>

    <reference id="weatherService"
               availability="mandatory"
               interface="com.myslyv4uk.weather.api.WeatherService">
        <reference-listener ref="referenceListener"
                            unbind-method="unbind" />
    </reference>

    <bean id="showWeatherImpl" class="com.myslyv4uk.client.impl.ShowWeatherServiceImpl"
        init-method="start" destroy-method="stop" >
        <argument ref="weatherService" />
    </bean>

</blueprint>

The referenceListener bean is called when the service is removed. By injecting the bundle context, you can stop the bundle itself:

public class ReferenceListener {

private Logger log; // set this up
private BundleContext bundleContext;

public void setBundleContext(BundleContext bundleContext) {
    this.bundleContext = bundleContext;
}

// Called when the service is injected
public void bind(ServiceReference<?> sr) {
    log.info("Bind of ServiceReference {} to bundle {}",
            sr, bundleContext.getBundle());
}

// Called when the service is removed
public void unbind(ServiceReference<?> sr) {
    log.info("Unbind of ServiceReference {} from bundle {}",
            sr, bundleContext.getBundle());
    try {
        if (bundleContext.getBundle().getState() == Bundle.ACTIVE) {
            log.warn("Bundle {} will be stopped after unbind of mandatory ServiceReference {}",
                    bundleContext.getBundle(), sr);
            bundleContext.getBundle().stop();
        }

    } catch (BundleException e) {
        log.error("Cannot stop bundle {} after unbind of ServiceReference {}",
                bundleContext.getBundle().getBundleId(),
                sr,
                e);
    }
}
}

This solution works but has some drawbacks, for example if you restart the container the service is removed, thus setting the bundle in STOPPED state.

Alessandro Da Rugna
  • 4,571
  • 20
  • 40
  • 64
  • Stopping your own bundle programmatically is a terrible thing to do. On StackOverflow you shouldn't always give the questioner exactly what they want, especially if they are asking for something that will harm them. – Neil Bartlett Oct 18 '17 at 11:13
  • @NeilBartlett Thanks for the comment, I agree with you and put a warning on top of the post. Since this is what OP was asking for I'll leave it here for reference, any other Blueprint compatible solutions are welcome :-) – Alessandro Da Rugna Oct 18 '17 at 11:55
  • Thanks, @AlessandroDaRugna your answer gave me a hint how to solve my problem in production. Actually, on stopping service I needed to terminate some logic and unbind0method helped me in this case. Thanks a lot – Bohdan Myslyvchuk Oct 23 '17 at 10:46