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

来源:互联网 发布:网络最新骗术扒人 编辑:程序博客网 时间:2024/05/08 16:59
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.  
  6. publicclass Activator implements BundleActivator {  
  7. ServiceReference helloServiceReference;  
  8. public void start(BundleContext context)throws Exception {  
  9. System.out.println("HelloWorld!!");  
  10. helloServiceReference=context.getServiceReference(HelloService.class.getName());  
  11. HelloService helloService=(HelloService)context.getService(helloServiceReference);  
  12. System.out.println(helloService.sayHello());  
  13.  
  14. }  
  15. public void stop(BundleContext context)throws Exception {  
  16. System.out.println("Goodbye World!!");  
  17. context.ungetService(helloServiceReference);  
  18. }  
  19. }  

在上面的代码中,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.  
  3.     public HelloServiceTracker(BundleContext context) {  
  4.         super(context, HelloService.class.getName(),null);  
  5.     }  
  6.     public Object addingService(ServiceReference reference) {  
  7.         System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());  
  8.         return super.addingService(reference);  
  9.     }  
  10.     public void removedService(ServiceReference reference, Object service) {  
  11.         System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());  
  12.         super.removedService(reference, service);  
  13.     }  
  14. }  

在构造函数中,把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对象。
原创粉丝点击