简体   繁体   中英

Embedded OSGi: accessing dynamically loaded bundle components

I'm getting a little lost when dealing with embedding OSGi into an application/library, loading a bundle at runtime and then using that bundle in the host application/library.

This is code to load the bundle from my host:

// framework is the embedded OSGi framework
Bundle b = framework.getBundleContext().installBundle( "file:/project_dir/bundle/MyBundle.jar" );
b.start();

And the bundle consists of something really simple:

public class Activator implements BundleActivator
{
  @Override
  public void start( BundleContext context )
  {
  }

  @Override
  public void stop( BundleContext context )
  {
  }

  public String shout()
  {
    return "let it all out";
  }
}

How would I access the shout() method from the host application?

In short:

  • Register your objects as OSGi services
  • Get the OSGi services in the application/library that started the embedded OSGi container

More detailed solution:

  • Define an interface in a separate jar that contains the shout() function
  • Place the jar file on the classpath of the application/library
  • Implement the interface in the Activator class of your bundle
  • Register the instance of the Activator class as an OSGi service based on the interface
  • In the application/library, get the OSGi service based on the interface

Alternatively, if you do not have the interface in the application/library, you can still get the OSGi service and call the function using reflection.

Here are some details about the code to use following the previous answer:

  • The interface containing your method.

     public interface MyInterface { String shout(); } 

    You can notice that it's a good approach to have two bundles for your library: one for the interface, and another for the implementation. It will prevent from having "refresh packages" issues when the implementation bundle went and come again.

  • The implementation of the previous interface:

     public class MyImpl implements MyInterface { public String shout() { return "let it all out"; } } 

    The package of the implementation doesn't need to be exported by the bundle. The class MyImpl won't be use directly by service consumers.

  • The new code of the activator:

     public class Activator implements BundleActivator { private ServiceRegistration serviceRegistration; @Override public void start( BundleContext context ) { this.serviceRegistration = context.registerService( MyInterface.class, new MyImpl(), new HashMap<String,String>()); } @Override public void stop( BundleContext context ) { if (serviceRegistration!=null) { serviceRegistration.unregister(); } } } 

    The bundle containing the activator and the implementation needs to import the package of the interface in its file MANIFEST.MF . Neither the package of the activator or the implementation needs to be exported within this file. They must remain internal to the bundle.

  • The class that consumes the service from another bundle

     public void callService() { ServiceReference<MyInterface> reference = bundleContext.getServiceReference(MyInterface.class); MyInterface foo = bundleContext.getService(reference); try { //use service String msg = service.shout(); } finally { bundleContext.ungetService( reference ); } } 

    You can notice that the consumers only requires to import the package of the interface (not the implementation one). Moreover you need to be careful to handle dynamics of OSGi. The service could have gone between two calls!

Hope it helps you, Thierry

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM