OSGi入门必读系列《OSGi服务:非常适合SOA的架构》

  • Post author:
  • Post category:其他


OSGi是一个非常适合实现面向服务的应用(SOA)。
可以让Bundles导出服务,而其他Bundles可以在不了解源Bundles任何信息的情况下消费这些导出的服务。
(1)导出服务:
更新HelloService Bundle,以便能把HelloServiceImpl类的对象导出为服务。(接前面的内容)
A、确认在HelloService Bundle中的MANIFEST.MF文件中导入了org.osgi.framework包
B、在HelloService Bundle中的src文件下创建一个在com.javaworld.sample.helloservice.impl包下的HelloServiceImpl.java文件,代码如下:



  1. public


    class


    HelloServiceActivator


    implements


    BundleActivator {
  2. ServiceRegistrationhelloServiceRegistration;
  3. public void start(BundleContext context)throws Exception {
  4. HelloService helloService = newHelloServiceImpl();
  5. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
  6. }
  7. public void stop(BundleContext context)throws Exception {
  8. helloServiceRegistration.unregister();
  9. }
  10. }
在源Bundle中,使用BundleContext.registerService()方法来导出服务,该方法有三个参数:
i)第一个参数是要注册的服务的接口名称。
如果想把服务注册到多个接口下,需要新建一个String数组存放这些接口名,然后把这个数组作为第一个参数传给registerService()方法。上面代码中,想把服务导出到HelloService接口名下。
ii)第二个参数是要注册的服务的实际JAVA对象。上面代码中,导出HelloServiceImpl类的对象,并将其作为服务。
iii)第三个参数为服务的属性,是一个Dictionary对象。如果多个Bundle导出服务的接口名相同,目标Bundle就可以使用这些属性对源进行过滤,找到感兴趣的服务。
C、最后修改HelloService Bundle中的MANIFEST.MF文件中的Bundle-Activator属性头的值,改为第二步新建文件,即“com.javaworld.sample.helloservice.impl.HelloServiceActivator”。
现在HelloService Bundle就可以导出HelloServiceImpl对象了。
当OSGi容器启动HelloService Bundle时,会将控制权交给HelloServiceActivator.java类,HelloServiceActivator将HelloServiceImpl对象注册为服务。
下面开始创建该服务的消费者。
(2)导入服务:
修改HelloWorld Bundle,以便让它成为HelloService服务的消费者。主要需要修改HelloWorld Bundle中的Activator.java文件。
源码如下:
  1. importorg.osgi.framework.BundleActivator;
  2. importorg.osgi.framework.BundleContext;
  3. importorg.osgi.framework.ServiceReference;
  4. importcom.javaworld.sample.service.HelloService;
  5. publicclass Activator implements BundleActivator {
  6. ServiceReference helloServiceReference;
  7. public void start(BundleContext context)throws Exception {
  8. System.out.println(“HelloWorld!!”);
  9. helloServiceReference=context.getServiceReference(HelloService.class.getName());
  10. HelloService helloService=(HelloService)context.getService(helloServiceReference);
  11. System.out.println(helloService.sayHello());
  12. }
  13. public void stop(BundleContext context)throws Exception {
  14. System.out.println(“Goodbye World!!”);
  15. context.ungetService(helloServiceReference);
  16. }
  17. }
在上面的代码中,BundleContext.getServiceReference()方法将为注册在HelloService接口下的服务返回一个ServiceReference对象。
如果存在多个HelloService服务,该方法返回排行最高的服务(服务的排行是通过Constrains.SERVICE_RANKING属性指定)。
一旦获得ServiceReference对象,就可以调用BundleContext.getService()方法获得真实的服务对象。
参照运行Bundle的方法运行上面的示例,点击run菜单,并确保同时选中两个Bundle。当启动HelloServiceBundle时,会在控制台看到“InsideHelloServiceImpl.sayHello()”,这个消息是由HelloServiceImpl.sayHello()方法打印出来的。
(3)创建服务工厂:
在上面的示例代码中,使用OSGi框架新建一个Java对象,并把它注册为一个服务,然后让其他的Bundle来消费这个服务。在HelloServiceActivator.start()中,注意到在start()方法中新建了HelloServiceImpl的对象,然后将它注册到HelloService接口名下。代码如下:
  1. HelloService helloService = newHelloServiceImpl();
  2. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);

这样注册后,任何其他的Bundle在请求HelloService服务时,OSGi容器将返回同一对象。
[prob]如果要为每个Bundle消费者返回不同的HelloServiceImpl对象,或者服务对象要提供的服务为一个数据库连接,但不是马上打开而是在真正需要的时候才打开这个数据库连接。这时应该如何处理?
[ans]新建一个类实现ServiceFactory接口,并把该类注册为服务,但不是注册实际的服务对象。如果完成了该步骤,其他Bundle在请求该服务时,ServiceFactory实现类将接管该请求,为每个Bundle新建一个服务对象,并把真实服务的创建时间延迟到有人真正需要该服务的时候。
A、新建工厂类HelloServiceFactory.java文件,代码如下:


  1. public


    class


    HelloServiceFactory


    implements


    ServiceFactory{
  2. private int usageCounter = 0;
  3. public Object getService(Bundle bundle,ServiceRegistration registration) {
  4. System.out.println(“Create objectof HelloService for ” + bundle.getSymbolicName());
  5. usageCounter++;
  6. System.out.println(“Number ofbundles using service ” + usageCounter);
  7. HelloService helloService = newHelloServiceImpl();
  8. return helloService;
  9. }
  10. public void ungetService(Bundle bundle,ServiceRegistration registration, Object service) {
  11. System.out.println(“Release objectof HelloService for ” + bundle.getSymbolicName());
  12. usageCounter–;
  13. System.out.println(“Number ofbundles using service ” + usageCounter);
  14. }
  15. }
从上面代码可以看到,ServiceFactory接口定义了两个方法:
i)getService()方法:当某一个Bundle第一次使用BundleContext.getService(ServiceReference)方法请求一个服务对象时,OSGi会调用该方法。
使用该方法可以为每一个Bundle返回不同的HelloServiceImpl对象。
若该对象不为null,OSGi会缓存这个对象,如果同一个bundle再次调用该方法,OSGi会返回同一个服务对象。
ii)ungetService()方法:当Bundle释放服务时,OSGi容器调用该方法销毁服务对象。
B、修改HelloService Bundle中的HelloServiceActivator.java的start()方法,让它注册到ServiceFactory接口名下。
  1. publicclass HelloServiceActivator implements BundleActivator {
  2. ServiceRegistrationhelloServiceRegistration;
  3. public void start(BundleContext context)throws Exception {
  4. HelloServiceFactory helloServiceFactory= new HelloServiceFactory();
  5. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);
  6. }
  7. public void stop(BundleContext context)throws Exception {
  8. helloServiceRegistration.unregister();
  9. }
  10. }
(4)跟踪服务:
当有多个Bundle使用同一接口名注册服务,这是OSGi容器会返回排行最高的服务,也就是注册时SERVICE_RANKING属性值最大的服务。如果有多个服务排行值相同,选择pid最小的那个服务。
如果服务消费者想了解某一接口的服务对象何时注册、何时取消注册,应该属用ServiceTracker类。
A、修改HelloWorld Bundle中的MANIFEST.MF文件,使其导入org.osgi.util.tracker包
B、新建类HelloServiceTracker.java文件,使其实现ServiceTracker类,代码如下:


  1. public


    class


    HelloServiceTracker


    extends


    ServiceTracker {
  2. public HelloServiceTracker(BundleContext context) {
  3. super(context, HelloService.class.getName(),null);
  4. }
  5. public Object addingService(ServiceReference reference) {
  6. System.out.println(“Inside HelloServiceTracker.addingService ” + reference.getBundle());
  7. return super.addingService(reference);
  8. }
  9. public void removedService(ServiceReference reference, Object service) {
  10. System.out.println(“Inside HelloServiceTracker.removedService ” + reference.getBundle());
  11. super.removedService(reference, service);
  12. }
  13. }
在构造函数中,把HelloService接口名传入其父类中,相当于说,HelloServiceTracker应跟踪注册到HelloService接口名下的所有服务。
HelloServiceTracker继承ServiceTracker,实现了两个方法:
i)addingService()方法:当Bundle使用接口名注册服务时,该方法会被调用。
ii)removingService()方法:当Bundle取消注册某个接口名下的服务时,该方法将被调用。
C、使用HelloServiceTracker类更新Activator.java类,以便让它来管理服务,而不是直接去查找他们。
HelloService Bundle的Activator.java文件源码如下:


  1. public


    class


    Activator


    implements


    BundleActivator {
  2. HelloServiceTracker helloServiceTracker;
  3. public void start(BundleContext context) throws Exception {
  4. System.out.println(“Hello World!!”);
  5. helloServiceTracker= new HelloServiceTracker(context);
  6. helloServiceTracker.open();
  7. HelloService helloService = (HelloService)helloServiceTracker.getService();
  8. System.out.println(helloService.sayHello());
  9. }
  10. public void stop(BundleContext context) throws Exception {
  11. System.out.println(“Goodbye World!!”);
  12. helloServiceTracker.close();
  13. }
  14. }
在start()方法中,首先新建一个HelloServiceTracker对象,然后要求这个对象跟踪HelloService接口下的服务。可以调用getService()方法来获得HelloService对象。