How to consume OSGi service from OSGi HTTP Service

2019-08-29 02:40发布

I have a bundle A which exposes the following service:

In OSGI-INF/config.xml

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
    name="com.example.MyService" modified="updated" immediate="true">
 <implementation class="com.example.impl.MyServiceImpl"/>
 <service>
  <provide interface="com.example.MyService"/>
 </service>
</scr:component>

Next step, I want to consume this service from a servlet in bundle B.

What I do is the following:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
 BundleContext bundleContext = (BundleContext) getServletContext().getAttribute("osgi-bundlecontext");
    if (bundleContext != null) {
     // Here MyService is the service exposed as declarative service
     MyService myService = getService(bundleContext, MyService.class);
     if(myService != null) {
      // I want to invoke some method declared in MyService interface
      myService.invokeMyServiceMethod();
     }
    }
}// end of doPost

protected <T> T getService(BundleContext bundleContext, Class<T> type) {
    ServiceReference<T> serviceRef = bundleContext.getServiceReference(type);
    if (serviceRef == null) {
        return null;
    }
    T service = bundleContext.getService(serviceRef);
    return service;
}// end of getService method

As services in OSGi come and go, is it correct to assume that even if the check for non null reference in doPost method passes, the next statement myService.invokeMyServiceMethod() will not throw NPE?

How can I guarantee that I will always get a valid reference to MyService from service registry?

If this is not the correct way of getting service reference from Http Service, what is the correct one?

I am using Equinox as OSGi implementation.

Cheers, Boris

标签: osgi
1条回答
Deceive 欺骗
2楼-- · 2019-08-29 03:18

I think you've missed a few bits of declarative services (DS) :-) The whole idea of DS that you specify your dependencies in the XML (or MUCH MUCH better with the annotations). This is how the servlet should look like:

@Component(provide=Servlet.class)
public class MyServlet extends HttpServlet {
  T myService;

  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
    myService.invokeMyServiceMethod();
  }

  @Reference
  T setT(T t) {
    myService =t;
  }
}

The only thing required is to ensure you have Apache Felix's Http Whiteboard bundle installed (yes, it works fine on Equinox, the beauty of standards). This bundle watches any Servlet services being registered and adds them to the Http Service. Since DS ensures that your component is not registered until it has the myService, you myService is guaranteed to be non-null. This is called the DS static mode: all your dependencies are satisfied before you get called.

If you're brave, you could declare the setT method to be dynamic. Then your component would be registered even if there is no T service. E.g. allows you to tell the caller there is no service. This is called the dynamic mode.

The used annotations are standard DS. They are processed by bnd and turned into the XML. This works in maven, gradle, etc. but best of all of course in bndtools.

查看更多
登录 后发表回答