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