OSGi入門必讀系列《OSGi服務:非常適合SOA的架構》

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對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章