OSGi服务:SOA的架构

OSGi服务:非常适合SOA的架构


本文介绍OSGi服务。OSGi架构非常适合我们实现面向服务的应用(SOA)。OSGi具有隐藏真实的服务实现类的能力,所以它为面向服务的应用提供了良好的类与接口的组合。

OSGi服务

前面我们提到,OSGi架构非常适合我们实现面向服务的应用(SOA)。它可以让Bundles导出服务,而其它的Bundles可以在不必了解源Bundles任何信息的情况下消费这些导出的服务。由于OSGi具有隐藏真实的服务实现类的能力,所以它为面向服务的应用提供了良好的类与接口的组合。

在OSGi框架中,源Bundle在OSGi容器中注册POJO对象,该对象不必实现任何接口,也不用继承任何超类,但它可以注册在一个或多个接口下,并对外提供服务。目标Bundle可以向OSGi容器请求注册在某一接口下的服务,一旦它发现该服务,目标Bundle就会将该服务绑定到这个接口,并能调用该接口中的方法。下面我们举个例子,以便我们能更好理解与OSGi相关的这些概念。

5.1. 导出服务

在本小节中,我们将更新HelloService Bundle,以便它能把HelloServiceImpl类的对象导出为服务,具体步骤如下:

1) 修改com.javaworld.sample.HelloService Bundle中的MANIFEST.MF文件,让它导入org.osgi.framework包(译者注,这一步我们已经完成);

2) 新建Java类com.javaworld.sample.impl.HelloServiceActivator.java,其源代码如清单7所示;

源代码清单7. HelloServiceActivator.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. }  
  11.  

请注意,在源Bundle中,我们应使用BundleContext.registerService()方法导出服务,这个方法带三个参数:

a) 该方法第一个参数为您要注册的服务的接口名称。如果您想把您的服务注册到多个接口下,您需要新建一个String数组存放这些接口名,然后把这个数组作为第一个参数传给registerService()方法。在示例代码中,我们想把我们的服务导出到HelloServer接口名下;

b) 第二个参数是您要注册的服务的实际Java对象。在示例代码中,我们导出HelloServiceImpl类的对象,并将其作为服务;

c) 第三个参数为服务的属性,它是一个Dictionary对象。如果多个Bundle导出服务的接口名相同,目标Bundle就可以使用这些属性对源Bundle进行过滤,找到它感兴趣的服务。

3) 最后,请修改HelloServiceBundle中的MANIFEST.MF文件,将Bundle-Activator属性头的值改为com.javaworld.sample.service.impl.HelloServiceActivator。

现在HelloService Bundle就可以导出HelloServiceImpl对象了。当OSGi容器启动HelloServiceBundle时,它会将控制权交给HelloServiceActivator.java类,HelloServiceActivator将HelloServiceImpl对象注册为服务。下面,我们开始创建该服务的消费者。

5.2. 导入服务

在本小节中,我们将修改上面开发的HelloWorld Bundle,以便让它成为HelloService服务的消费者。您主要需要修改HelloWorldBundle中的Activator.java代码,修改后的代码如源代码清单8所示:

源代码清单8. HelloWorld Bundle中的Activator.java

 
    
  1. packagecom.javaworld.sample.helloworld;  
  2.  
  3. importorg.osgi.framework.BundleActivator;  
  4. importorg.osgi.framework.BundleContext;  
  5. importorg.osgi.framework.ServiceReference;  
  6. importcom.javaworld.sample.service.HelloService;  
  7.  
  8. publicclass Activator implements BundleActivator {  
  9. ServiceReference helloServiceReference;  
  10. public void start(BundleContext context)throws Exception {  
  11. System.out.println("HelloWorld!!");  
  12. helloServiceReference=context.getServiceReference(HelloService.class.getName());  
  13. HelloService helloService=(HelloService)context.getService(helloServiceReference);  
  14. System.out.println(helloService.sayHello());  
  15.  
  16. }  
  17. public void stop(BundleContext context)throws Exception {  
  18. System.out.println("Goodbye World!!");  
  19. context.ungetService(helloServiceReference);  
  20. }  
  21. }  
  22.  

在上面的代码中,BundleContext.getServiceReference()方法将为注册在HelloService接口下的服务返回一个ServiceReference对象。如果存在多个HelloService服务,该方法会返回排行最高的服务(服务的排行是通过Constants.SERVICE_RANKING属性指定的)。您一旦获得ServiceReference对象,您就可以调用其BundleContext.getService()方法获取真实的服务对象。

您可以参照运行Bundle的方法运行上面的示例应用,请点击“RunàRun…”菜单,并确保HelloWorld和HelloService这两个Bundle被选中。当您启动HelloServiceBundle时,您会在控制台上看到“InsideHelloServiceImple.sayHello()”,这个消息是由HelloServiceImpl.sayHello()方法打印出来的。

5.3. 创建服务工厂

在上节中,我们学会了如何使用OSGi框架新建一个Java对象,并把它注册为一个服务,然后让其它的Bundle去消费这个服务。如果您看一下HelloServiceActivator.start()方法,您会注意到我们在start()方法中新建了HelloServiceImpl类对象,然后将它注册到HelloService接口名下。这样注册后,任何其它的Bundle在请求HelloService服务时,OSGi容器将返回同一对象。

在大多数情况下,这样的实现方法没有问题。但是,比如说我们要为每一个Bundle消费者返回不同的HelloServiceImpl对象,再比如说,您的服务对象要提供的服务为打开一个数据库连接,但并不是马上就打开它,而是在真正需要的时候才打开这个数据库连接。

对这两种情况,我们的解决方法是,新建一个类实现ServiceFactory接口,并把该类的对象注册为服务,但并不是注册实际的服务对象。一旦您完成这一步,其它Bundle在请求该服务时,您的ServiceFactory实现类将接管该请求,ServiceFactory会为每个Bundle新建一个服务对象,并将真实服务的创建时间延迟到有人真正需要该服务的时候。

下面我们将使用ServiceFactory更新我们上面开发的com.javaworld.sample.HelloServiceBundle,具体步骤如下:

1) 新建工厂 类HelloServiceFactory.java,源代码如清单9所示。

源代码清单9 . 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. }  
  16.  

从上面的代码中,我们可以看到,ServiceFactory接口定义了两个方法:

a) getService()方法:当某个Bundle第一次使用BundleContext.getService(ServiceReference)方法请求一个服务对象时,OSGi框架会调用该方法。在源代码清单9中,我们用这个方法为每个Bundle新建并返回不同的HelloServiceImpl对象,如果这个对象不是null,OSGi框架会缓存这个对象。如果同一个Bundle再次调用BundleContext.getService(ServiceReference)方法,OSGi将返回同一个服务对象。

b) ungetService()方法:当Bundle释放服务时,OSGi容器可以调用该方法销毁服务对象。在源代码清单9中,我们使用usageCounter变量来跟踪服务的使用数目,并打印出该服务的客户端数量。

2) 修改HelloService Bundle中的HelloServiceActivator.java的start()方法,让它注册到ServiceFactory接口名下,而不是注册到HelloService接口。详细代码如清单10所示:

源代码清单10. 修改后的HelloServiceBundle中的HelloServiceActivator.java

 
    
  1. package com.javaworld.sample.service.impl;  
  2. importorg.osgi.framework.BundleActivator;  
  3. importorg.osgi.framework.BundleContext;  
  4. importorg.osgi.framework.ServiceRegistration;  
  5.  
  6. importcom.javaworld.sample.helloservice.HelloServiceFactory;  
  7. importcom.javaworld.sample.service.HelloService;  
  8.  
  9. publicclass HelloServiceActivator implements BundleActivator {  
  10. ServiceRegistrationhelloServiceRegistration;  
  11. public void start(BundleContext context)throws Exception {  
  12. HelloServiceFactory helloServiceFactory= new HelloServiceFactory();  
  13. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);  
  14. }  
  15. public void stop(BundleContext context)throws Exception {  
  16. helloServiceRegistration.unregister();  
  17. }  
  18. }  
  19.  

现在,您可以试运行示例代码。您会注意到,当HelloWorld Bundle启动时,服务计数器变为1;当HelloWorldBundle停止时,服务计数器的数目将变为0。

5.4. 跟踪服务

在“OSGi服务”小节,您学会了如何使用服务的接口名搜索服务。但如果有多个Bundle使用同一接口名注册服务,那会发生什么呢?这时,OSGi容器将返回排行最高的服务,即,返回注册时那个SERVICE_RANKING属性值最大的服务。如果有多个服务的排行值相等,那么OSGi容器将返回PID值最小的那个服务。

但是,如果您的服务消费者需要了解某一接口下的服务对象何时注册、何时取消注册,这时,您应使用ServiceTracker类。下面,我们看看如何使用服务跟踪器来修改我们的示例代码,具体步骤如下。

1) 修改HelloWorldBundle的MANIFEST.MF文件,让它导入org.osgi.util.tracker包;

2) 新建类HelloServiceTracker.java,其源代码参见清单11。

源代码清单11.HelloServiceTracker.java

 
    
  1. public class HelloServiceTracker extends ServiceTracker {  
  2.  
  3.     public HelloServiceTracker(BundleContext context) {  
  4.  
  5.         super(context, HelloService.class.getName(),null);  
  6.  
  7.     }  
  8.  
  9.     public Object addingService(ServiceReference reference) {  
  10.  
  11.         System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());  
  12.  
  13.         return super.addingService(reference);  
  14.  
  15.     }  
  16.  
  17.     public void removedService(ServiceReference reference, Object service) {  
  18.  
  19.         System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());  
  20.  
  21.         super.removedService(reference, service);  
  22.  
  23.     }  
  24.  
  25. }  
  26.  

在上面的HelloSerivceTracker类的构造函数中,您可以看到,我们把HelloService接口名传入其父类中,这相当于说,HelloServiceTracker应跟踪注册到HelloService接口名下的所有服务,HelloServiceTracker继承自ServiceTracker类,实现了下面两个方法:

a) addingService()方法:当Bundle使用接口名注册服务时,该方法将会被调用;

b)removedService()方法:当Bundle取消注册某个接口名下的服务时,该方法将会被调用。

3) 用HelloServiceTracker类更新我们的Activator.java类,以便让它来管理服务,而不是直接去查找它们,源代码请参见清单12。

源代码清单12. 使用了HelloServiceTracker的Activator.java

 
    
  1. public class Activator implements BundleActivator {  
  2.  
  3.     HelloServiceTracker helloServiceTracker;  
  4.  
  5.     public void start(BundleContext context) throws Exception {  
  6.  
  7.         System.out.println("Hello World!!");  
  8.  
  9.         helloServiceTracker= new HelloServiceTracker(context);  
  10.  
  11.         helloServiceTracker.open();  
  12.  
  13.         HelloService helloService = (HelloService)helloServiceTracker.getService();  
  14.  
  15.         System.out.println(helloService.sayHello());  
  16.  
  17.    
  18.  
  19.     }  
  20.  
  21.     public void stop(BundleContext context) throws Exception {  
  22.  
  23.         System.out.println("Goodbye World!!");  
  24.  
  25.         helloServiceTracker.close();  
  26.  
  27.     }  
  28.  
  29. }  
  30.  

我们看到,在初始的start()方法中,我们首先新建一个HelloServiceTracker对象,然后要求这个对象跟踪HelloService接口下的服务。这时,我们可以调用getService()方法获得HelloService对象。

如果您试运行上面的示例代码,您会注意到,在启动或停止HelloSerivceBundle时,OSGi容器都会调用HelloServiceTracker对象的addingService()方法或removedService()方法。

你可能感兴趣的:(OSGI)