I have an OSGi component MyComponent
.
This component has reference to a service MyService
. Now MyService
has a couple of implementations MyServiceImpl1
and MyServiceImpl2
. MyComponent
also has property MyProperty
.
Now what I want is that whenever MyProperty
is 1, MyComponent.MyService
binds to MyServiceImpl1
. And if I change MyProperty
to 2, MyComponent.MyService
dynamically updates the MyService
binding to `MyServiceImpl2.
How do I acheive this? For reference, I am using Apache Felix container and would prefer to avoid lower level OSGi apis.
The easiest way to configure dependencies is with the '.target' property. This requires that the implementations register with an identifying property, lets say impl=1
. and impl=2
.
@Component(property="impl=1")
public class MyServiceImpl1 implements MyService {
}
@Component(property="impl=2")
public class MyServiceImpl2 implements MyService {
}
The component then could look like:
@Component
public class MyComponent {
@Reference(target="(impl=1)")
volatile MyService myService;
}
In this case you won't be able to use 1 or 2 as flag but you would have to modify the configuration property for MyComponent
with the name myService.target
with another filter. (This is shown with OSGi standardized annotations.)
If you insist on a property that is 1 or 2 (let's call it select
) for MyComponent
then it is more elaborate. First we have the problem of being satisfied. Should MyComponent be satisfied when it needs according to the select property impl 1 but only 2 is available? If that is ok then the following much more complicated solution should work
@Designate( ocd=Config.class )
@Component( property = "select=1" )
public class MyComponent {
static Class<?> types [] = {
MyServiceImpl1.class,
MyServiceImpl2.class,
};
@interface Config {
int select() default 1;
}
@Reference(target="(|(impl=1)(impl=2))")
volatile List<MyService> candidates;
volatile MyService selected;
@Activate
@Modify
void changed( Config config ) {
Class<?> type = types[config.select()];
Optional<MyService> service = candidates.
stream().
filter( type::isInstance ).
findFirst();
this.service = service.isPresent() ? service.get() : null;
}
}
As you can see it is generally a bad idea for a component to start to handle its own dependencies. I am therefore curious to your real world scenario.
I find it always very awkward and hard to maintain designs where components are picky about who they reference. It is sometimes inevitable but in general solutions where MyServiceImpl2
and MyServiceImpl1
decide to register or not based on some condition reflect reality better.
So the big question I have what does the 1 or 2 property reflect in the real world? Can this not be modeled as a service dependency?
(disclaimer: code not tested and no error handling in it)
I'm assuming implementation of MyService
can be queried to report its type (e.g. below):
public interface MyService {
public static final String TYPE = "myservice.type";
}
If so, for a Declarative Service OSGi component on Apache Felix, here's an approach:
MyService
reference in MyComponent
with
Dynamic
Reference Policy (policy = ReferencePolicy.DYNAMIC
)1..n
cardinality (cardinality = ReferenceCardinality.MANDATORY_MULTIPLE
)bind/unbind
methods for MyService
reference in MyComponent
Modified
method in MyComponent
bind/unbind
methods of MyComponent
will called as and when MyService
implementations are instantiated by Felix SCR. You would want to maintain a map of available implementations.
Modified
method will be called whenever there's a configuration-update-event for MyComponent
. In this method, based on the updated Component Configuration Property, appropriate method can be selected for further processing.
This is how the component would look like when using Felix SCR annotations.
@Component (metatype = true, immediate = true)
@Service (value = MyComponent.class)
public class MyComponent {
@Property(name = "impl.selector", value = "impl_1")
private String implSelector = "impl_1";
@Reference(
referenceInterface = MyService.class,
policy = ReferencePolicy.DYNAMIC,
cardinality = ReferenceCardinality.MANDATORY_MULTIPLE,
strategy = ReferenceStrategy.EVENT,
bind = "bindService",
unbind = "unbindService"
)
private Map<String, MyService> availableMyServiceImpls = new HashMap<String, MyService>();
private MyService service = null;
@Activate
public void activate(ComponentContext componentContext) {
service = availableMyServiceImpls.get(implSelector);
}
@Deactivate
public void deactivate(ComponentContext componentContext) {
availableMyServiceImpls.clear();
}
public void bindService(MyService serviceRef, Map<?,?> refProperties) {
String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY);
availableMyServiceImpls.put(serviceImplName, serviceRef);
}
public void unbindService(MyService serviceRef, Map<?,?> refProperties) {
String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY);
availableMyServiceImpls.remove(serviceImplName);
}
@Modified
public void modified(ComponentContext componentContext) {
Dictionary<String, Object> componentProps = componentContext.getProperties();
implSelector = PropertiesUtil.toString(componentProps.get("impl.selector"), "");
service = availableMyServiceImpls.get(implSelector);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With