Reputation: 461
I've spent a long time on learning OSGi, but I still feel as though a crucial piece of the puzzle is missing.
This is my use case:
I am using JAX-RS (Grizzly) to create a REST API. I have an interface that has many various implementations. My solution should be able to add new implementations at any time.
Based on some form of input, I have to get a hold of one these specific instances. For example, lets say I register two interface implementations with Felix Interface A
and Interface B
. A user should be able to ask for Implementation B
By using the command line we get from running felix.jar
(Apache Felix Gogo ) I have been able to install and start my own bundles. The problem I now face is how I from one of my controllers is supposed to retrieve any of these implementation.
Here is the code for the activator of one of my implementations.
public class MyClassActivator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
System.out.println("Starting ImplementationA");
Hashtable<String, String> props = new Hashtable<>();
props.put("Identifier", "ImplementationA");
context.registerService(MyInterface.class.getName(), new MyClassA(), props);
}
@Override
public void stop(BundleContext context) throws Exception {
System.out.println("Stopping ImplementationA");
}
private class ImplementationA implements MyInterface {
/*my implementation*/
}
}
From one of my JAX-RS classes, I want to, somehow, do this:
MyInterface myclassA = getBundle("ImplementationA");
The String ImplementationA
is the same string I placed in the props map.
What I have tried so far is
BundleContext bc = FrameworkUtil.getBundle(MyInterface.class).getBundleContext();
This however just returns null, it doesn't seem to actually be "talking" to my felix instance.
So my questions are how do I get an interface from Felix? And is what I want to do even possible with OSGi?
Upvotes: 3
Views: 508
Reputation: 9384
Your question is confusing since you mix terms for services and bundles. A bundle is an installable unit which contains code. That code can register and consume services. Services are object which, typically, implement some interface which is shared between the bundle providing the service and the bundles consuming the services.
So the first order of business is to make sure the service interface's package is exported by some bundle and imported by all the bundles which plan to participate in providing and consuming the service. This is necessary to ensure type safety. That is, the consuming bundles can safely cast the service object to the expected type of the service.
Once that is done, as you observer, there can be multiple providers of a service. When a provider registers a service, they can specify some metadata about the service in the form of key/value properties. Your example shows this in the Identifier
property. When a consumer looks up a service, a filter string can be specified which can specify information to be checked against a service' metadata to select from among multiple provided services.
public class MyServiceConsumer implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
System.out.println("Looking for ImplementationA");
ServiceReference<MyInterface>[] refs =
context.getServiceReferences(MyInterface.class, "(Identifier=ImplementationA)");
MyInterface service = context.getService(refs[0]);
}
}
The above is terrible code; don't actually use it. It does not handle there being no service when the consumer bundle is activated (refs == null), nor does is it prepared for the service to go away. I strongly recommend you use OSGi Declarative Services when writing bundles. It makes service use and dealing with the dynamic super simple.
@Component
public class MyServiceConsumer {
MyInterface service;
@Reference(target="(Identifier=ImplementationA)")
private void bindService(MyInterface s) {
service = s;
}
@Activate
private activate() {
// do work
}
@Deactivate
private deactivate() {
// do work
}
}
This is a component which will only be instantiated when a matching service is present. It will be called at bindService to inject the service instance, the activate will be called to enable to component to do it work. If the injected service goes away, the component will be called at deactivate and then discarded. If later another matching service comes along, a new instance of the component will be activated.
See http://enroute.osgi.org/ for a tutorial on OSGi app dev.
Upvotes: 5