views:

1683

answers:

3

I am just getting started with OSGI and Declarative Services (DS) using Equinox and Eclipse PDE.

I have 2 Bundles, A and B. Bundle A exposes a component which is consumed by Bundle B. Both bundles also expose this service to the OSGI Service registry again.

Everything works fine so far and Equinox is wireing the components together, which means the Bundle A and Bundle B are instanciated by Equinox (by calling the default constructor) and then the wireing happens using the bind / unbind methods.

Now, as Equinox is creating the instances of those components / services I would like to know what is the best way of getting this instance?

So assume there is third class class which is NOT instantiated by OSGI:

Class WantsToUseComponentB{
public void doSomethingWithComponentB(){
 // how do I get componentB??? Something like this maybe?
 ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());
}
I see the following options right now:

1. Use a ServiceTracker in the Activator to get the Service of ComponentBundleA.class.getName() (I have tried that already and it works, but it seems to much overhead to me) and make it available via a static factory methods
 
public class Activator{

   private static ServiceTracker componentBServiceTracker;   

   public void start(BundleContext context){

     componentBServiceTracker = new ServiceTracker(context, ComponentB.class.getName(),null);
   }

   public static ComponentB getComponentB(){
     return (ComponentB)componentBServiceTracker.getService(); 
   };

}

2. Create some kind of Registry where each component registers as soon as the activate() method is called.

public ComponentB{

public void bind(ComponentA componentA){
   someRegistry.registerComponent(this);
}

or

public ComponentB{

   public void activate(ComponentContext context){
      someRegistry.registerComponent(this);
   }

}

}

3. Use an existing registry inside osgi / equinox which has those instances? I mean OSGI is already creating instances and wires them together, so it has the objects already somewhere. But where? How can I get them?

Conclusion Where does the class WantsToUseComponentB (which is NOT a Component and NOT instantiated by OSGI) get an instance of ComponentB from? Are there any patterns or best practises? As I said I managed to use a ServiceTracker in the Activator, but I thought that would be possible without it.

What I am looking for is actually something like the BeanContainer of Springframework, where I can just say something like Container.getBean(ComponentA.BEAN_NAME). But I don't want to use Spring DS.

I hope that was clear enough. Otherwise I can also post some source code to explain in more detail.

Thanks Christoph


UPDATED: Answer to Neil's comment:

Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.

Hmm don't know. Maybe there is a way but I would need to refactor my whole framework to be based on DS, so that there are no "new MyThirdClass(arg1, arg2)" statements anymore. Don't really know how to do that, but I read something about ComponentFactories in DS. So instead of doing a

MyThirdClass object = new MyThirdClass(arg1, arg2);

I might do a

ComponentFactory myThirdClassFactory = myThirdClassServiceTracker.getService(); // returns a 

if (myThirdClassFactory != null){
  MyThirdClass object = objectFactory.newInstance();

   object.setArg1("arg1");
  object.setArg2("arg2");
}
else{
 // here I can assume that some service of ComponentA or B went away so MyThirdClass Componenent cannot be created as there are missing dependencies?

}

At the time of writing I don't know exactly how to use the ComponentFactories but this is supposed to be some kind of pseudo code :)

Thanks Christoph

A: 

christoph, dont know if I really understand your problem. per ex. Bundle A is providing a service using DS component:

<service>
  <provide interface="org.redview.lnf.services.IRedviewLnfSelectedService"/>

Bundle B requires this service using DS component:

<implementation class="ekke.xyz.rcp.application.internal.XyzApplicationLnfComponent"/>

as soon as Bundle A provides the Service, Bundle B "gets" it through the bind() methode of the implementation class:

public class XyzApplicationLnfComponent {
public void bind(IRedviewLnfSelectedService lnfSelectedService) {
 // here it is
}

hope this helps ekke

ekkescorner
bundle B also requires the service (line was 'gone' after publishing my answer)<reference bind="bind" cardinality="1..1" interface="org.redview.lnf.services.IRedviewLnfSelectedService" name="RedviewLnfSelectedService" policy="static"/>
ekkescorner
Thanks Ekke for your answer:I updadated my question with more details and code examples.What I am interested in is actually a 3rd class like this:<pre>Class WantsToUseComponentB{public void doSomethingWithComponentB(){ // how do I get componentB??? Something like this maybe? ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());}</pre>Could you have a look again? Thanks Christoph
Christoph
+3  A: 

Christoph,

Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.

DS causes components to be published as services, therefore the only way to "get" any component from DS is to access it via the service registry. Unfortunately the service registry can be hard to use correctly using the lower level APIs because it is dynamic, so you have to cope with the possibility of services going away or not being available at precisely the moment you want them to be available, and so on. This is why DS exists: it gives you an abstraction for depending on services and managing the lifecycle of your components based on the availability of services that they reference.

If you really need to access a service without using DS or something like it (and there is quite a choice of "things like it" e.g. Spring-DM, iPOJO, Guice/Peaberry, etc) then you should use ServiceTracker. I agree there is a lot of overhead -- again, this is why DS exists instead.

To answer your suggestion no (2), no you should not create your own registry of services because the service registry already exists. If you created a separate parallel registry then you would still have to handle all the dynamics, but you would have to handle it in two places instead of one. The same applies to suggestion (3).

I hope this helps.

Regards, Neil

UPDATED: Incidentally, although Spring has the Container.getBean() backdoor, you notice that in all Spring documentation it is strongly recommended not to use that backdoor: to get hold of a Spring bean, just create another bean that references it. The same applies to DS, i.e. the best way to get hold of a DS component is to create another DS component.

Also note that in the OSGi world, even if you're using Spring-DM there is no easy way to just call getBean() because you need to get hold of the Spring ApplicationContext first. That is itself an OSGi service, so how to you get that service?

Neil Bartlett
Thanks for you answer Neil. Ok assume the 3rd class is some class inside my own legacy framework and there is an object which cannot be created by DS.I would like to know if the way of the static ServiceTracker and the static factory method inside the Activator is a good way? To me it seems a bit hacky, but don't know why. Don't want to use static ServiceTrackers all over the place but right now that's how I do it. Is there a better approach? I guess there is :)ThanksChristoph
Christoph
Hi Neil,I updated the questions again (see the UPDATE at the bottom) to answer your initial question.
Christoph
A: 

Easy way: Inject the DS component into your Activator class with Riena: http://wiki.eclipse.org/Riena_Getting_Started_with_injecting_services_and_extensions

Then you can call it from everywhere: Activator.getDefault().getWhateverService()

stefan