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文件,代碼如下:
- public class HelloServiceActivator implements BundleActivator {
- ServiceRegistrationhelloServiceRegistration;
- public void start(BundleContext context)throws Exception {
- HelloService helloService = newHelloServiceImpl();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
- }
- public void stop(BundleContext context)throws Exception {
- helloServiceRegistration.unregister();
- }
- }
在源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文件。
源碼如下:
- importorg.osgi.framework.BundleActivator;
- importorg.osgi.framework.BundleContext;
- importorg.osgi.framework.ServiceReference;
- importcom.javaworld.sample.service.HelloService;
- publicclass Activator implements BundleActivator {
- ServiceReference helloServiceReference;
- public void start(BundleContext context)throws Exception {
- System.out.println("HelloWorld!!");
- helloServiceReference=context.getServiceReference(HelloService.class.getName());
- HelloService helloService=(HelloService)context.getService(helloServiceReference);
- System.out.println(helloService.sayHello());
- }
- public void stop(BundleContext context)throws Exception {
- System.out.println("Goodbye World!!");
- context.ungetService(helloServiceReference);
- }
- }
在上面的代碼中,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接口名下。代碼如下:
- HelloService helloService = newHelloServiceImpl();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);
這樣註冊後,任何其他的Bundle在請求HelloService服務時,OSGi容器將返回同一對象。
[prob]如果要爲每個Bundle消費者返回不同的HelloServiceImpl對象,或者服務對象要提供的服務爲一個數據庫連接,但不是馬上打開而是在真正需要的時候纔打開這個數據庫連接。這時應該如何處理?
[ans]新建一個類實現ServiceFactory接口,並把該類註冊爲服務,但不是註冊實際的服務對象。如果完成了該步驟,其他Bundle在請求該服務時,ServiceFactory實現類將接管該請求,爲每個Bundle新建一個服務對象,並把真實服務的創建時間延遲到有人真正需要該服務的時候。
A、新建工廠類HelloServiceFactory.java文件,代碼如下:
- public class HelloServiceFactory implements ServiceFactory{
- private int usageCounter = 0;
- public Object getService(Bundle bundle,ServiceRegistration registration) {
- System.out.println("Create objectof HelloService for " + bundle.getSymbolicName());
- usageCounter++;
- System.out.println("Number ofbundles using service " + usageCounter);
- HelloService helloService = newHelloServiceImpl();
- return helloService;
- }
- public void ungetService(Bundle bundle,ServiceRegistration registration, Object service) {
- System.out.println("Release objectof HelloService for " + bundle.getSymbolicName());
- usageCounter--;
- System.out.println("Number ofbundles using service " + usageCounter);
- }
- }
從上面代碼可以看到,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接口名下。
- publicclass HelloServiceActivator implements BundleActivator {
- ServiceRegistrationhelloServiceRegistration;
- public void start(BundleContext context)throws Exception {
- HelloServiceFactory helloServiceFactory= new HelloServiceFactory();
- helloServiceRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);
- }
- public void stop(BundleContext context)throws Exception {
- helloServiceRegistration.unregister();
- }
- }
(4)跟蹤服務:
當有多個Bundle使用同一接口名註冊服務,這是OSGi容器會返回排行最高的服務,也就是註冊時SERVICE_RANKING屬性值最大的服務。如果有多個服務排行值相同,選擇pid最小的那個服務。
如果服務消費者想了解某一接口的服務對象何時註冊、何時取消註冊,應該屬用ServiceTracker類。
A、修改HelloWorld Bundle中的MANIFEST.MF文件,使其導入org.osgi.util.tracker包
B、新建類HelloServiceTracker.java文件,使其實現ServiceTracker類,代碼如下:
- public class HelloServiceTracker extends ServiceTracker {
- public HelloServiceTracker(BundleContext context) {
- super(context, HelloService.class.getName(),null);
- }
- public Object addingService(ServiceReference reference) {
- System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle());
- return super.addingService(reference);
- }
- public void removedService(ServiceReference reference, Object service) {
- System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle());
- super.removedService(reference, service);
- }
- }
在構造函數中,把HelloService接口名傳入其父類中,相當於說,HelloServiceTracker應跟蹤註冊到HelloService接口名下的所有服務。
HelloServiceTracker繼承ServiceTracker,實現了兩個方法:
i)addingService()方法:當Bundle使用接口名註冊服務時,該方法會被調用。
ii)removingService()方法:當Bundle取消註冊某個接口名下的服務時,該方法將被調用。
C、使用HelloServiceTracker類更新Activator.java類,以便讓它來管理服務,而不是直接去查找他們。
HelloService Bundle的Activator.java文件源碼如下:
- public class Activator implements BundleActivator {
- HelloServiceTracker helloServiceTracker;
- public void start(BundleContext context) throws Exception {
- System.out.println("Hello World!!");
- helloServiceTracker= new HelloServiceTracker(context);
- helloServiceTracker.open();
- HelloService helloService = (HelloService)helloServiceTracker.getService();
- System.out.println(helloService.sayHello());
- }
- public void stop(BundleContext context) throws Exception {
- System.out.println("Goodbye World!!");
- helloServiceTracker.close();
- }
- }
在start()方法中,首先新建一個HelloServiceTracker對象,然後要求這個對象跟蹤HelloService接口下的服務。可以調用getService()方法來獲得HelloService對象。