OSGi 中的 Declarative Services 規範簡介

Declarative Services簡介

Declarative Services 是一個面向服務的組件模型,它制訂的目的是更方便地在 OSGi 服務平臺上發佈、查找、綁定服務,對服務進行動態管理,如監控服務狀態以及解決服務之間的複雜的依賴關係等問題。Declarative Services 採用服務組件的延遲加載以及組件生命週期管理的方式來控制對於內存的佔用以及啓動的快速,很好的解決了傳統的 OSGi 服務模型在開發和部署比較複雜應用時內存佔用大、啓動慢等問題,而且對服務組件的描述採用XML來實現,十分便於用戶理解和使用。在 Declarative Services 中,Component 能夠是 Service 的提供者和引用者,一個 Component 能夠提供 0 至多個 Service,也能夠引用 0 至多個 Service,而且採用component 方式封裝 Service,方便了對 Service 的複用,從開發者的角度來看,該服務組件模型簡化了在 OSGi 服務平臺中的編程模型。Declarative Services 規範參考了"Automating Service Dependency Management in a Service-Oriented Component Model"一文的有關概念,讀者可從參考資料得到該文的詳細信息。 html

回頁首 java

Component Satisfied 概念介紹

在 Declarative Services 中,一個服務組件是包含在 Bundle 應用中的普通的 Java 類,每一個Component 能夠暴露出多個服務,同時也可依賴於多個服務,經過XML文件描述和服務組件相關的信息,SCR(Service Component Runtime)根據服務組件配置文件控制着組件配置的激活(Activate)和鈍化(Deactivate),服務組件配置文件包括如組件的類型、組 件的實現以及引用的服務等信息。在詳細介紹服務組件(Component)以前,咱們必須瞭解 Component Satisfied 的概念,在 Declarative Services 中,Component Satisfied 與 Component 的生命週期密切相關。如 Component 激活的前提條件之一就是 Component Satisfied,而在 Component 的運行過程當中,出現 Unsatisfied 時,Component 將被鈍化。主要由如下兩點決定 Component 是否處於Satisfied 狀態: 編程

Component 爲 Enabled 狀態,Component 的生命週期包含在引用它的 Bundle 應用的生命週期以內,只有在 Bundle 處於 Active 狀態時,Component 纔有可能爲 Enabled 狀態,在 Bundle處於 Stop 狀態時,Bundle 中全部的 Component 都處在 Disabled 狀態。Component 初始的Enabled 狀態能夠在服務組件配置文件中設定。 框架

Component 的配置是能夠被引用和解析的,Component 中引用的 Service 也是 Satisfied 的,引用的 Service 至少有一個是處於可用狀態的,或者引用的 Service 在服務組件配置文件裏配置了可爲 0 個可用狀態的 Service。 eclipse

當上述兩個條件中任何一個不知足時,組件配置將變爲 Unsatisfied 狀態,組件配置將被鈍化。在理解 Component Satisfied 的概念後,下面講解三種類型的服務組件。 ide

回頁首 ui

Component 介紹

在 Bundle 啓動時, Declarative Services 裝載相應的服務組件配置文件,配置文件在MAINFEST.MF 文件的 Service-Component 屬性指定,解析配置文件,獲取服務組件引用的 Service ,若是判斷組件 Satisfied 狀態的兩個條件知足時, Declarative Services 就認爲這個組件是 Satisfied 的。 spa

Immediate Component .net

對 於 Immediate Component,若是組件配置處於 Satisfied 狀態,將會當即被激活,而且若是該配置指定了服務,那麼 SCR 會註冊該服務而且當即激活該服務組件。在 SCR 激活組件配置時,實現服務組件類的 activate 方法將會被調用,在SCR鈍化組件配置時,deactivate方法將會被調用。Immediate Component的狀態圖如圖1所示: 命令行

圖示1:Immediate Component狀態圖
圖示1:Immediate Component狀態圖

Delayed Component

對 於 Delayed Component ,若是組件配置處於Satisfied狀態,該組件並不會當即被激活,Declarative Services 會根據組件配置文件中的 Service 的配置,註冊相應的Service 的信息,直到該服務組件被請求時, Declarative Services 纔會激活該組件配置 。 Delayed Component 延遲了 Component 類的建立,當該服務組件的服務收到請求時,該 Component 類的 activate 方法纔會被調用。若是一個 Component 不是 Factory Component,而且在其組件配置文件中指定了服務,組件的 immediate 屬性設置爲 false,那麼該組件就是 Delayed Component。Delayed Component 的狀態圖如圖 2 所示:

圖示2:Delayed Component 狀態圖
圖示2:Delayed Component 狀態圖

Factory Component

通 過在組件配置文件中設置 Component 的 factory 屬性,將 Component 聲明爲 Factory Component。該組件在激活後註冊的是一個 Component Factory 服務,只有在調用 Component Factory 的 newInstance 方法後纔會激活相應的各個組件,每一次調用 newInstance 方法,都會建立和激活一個新的組件配置。若是在組件配置文件中聲明瞭服務,那麼在該組件激活以前,聲明的服務被註冊。Factory Component 的狀態圖如圖3所示:

圖示3:Factory Component狀態圖
圖示3:Factory Component狀態圖

在三種類型的服務組件中,Delayed Component 很好的解決了系統服務的動態性問題,同時也節省了內存的佔用。 服務組件的生命週期受 Bundle 生命週期影響,當 Bundle 中止時,那麼Bundle 中全部的服務組件也就中止。

回頁首

Service 的發佈、查找、綁定

在 OSGi 服務平臺中,大部分 Bundle 應用都是基於服務的,服務的發佈、引用十分重要,下面講一下利用服務組件如何進行 Service 的發佈、查找和綁定。

Service 的發佈

對 於 Component 中 Service 的發佈,須要在組件配置文件中定義 service 元素,該 service元素至少包括一個或多個 provide 元素,該 provide 元素定義了該 component 提供的服務接口,它只有一個屬性 interface,該 interface 定義了提供服務的接口,而且容許是實現該服務接口的類名。能夠看出,利用 Declarative Services 發佈 Service 很是簡單,只要 Component 實現了定義的 Service 的接口便可。如在本文所講解例子中,在組件配置文件中,聲明姓名查詢服務如圖 4 所示:

圖示4:姓名查詢服務聲明
圖示4:姓名查詢服務聲明

Service 的查找和綁定

在 Declarative Services 中,Component 所引用的服務,稱爲 Target Service,當 Component 中引用的 Target Service 也是 Satisfied 時,即引用的 Service 至少有一個是處於可用狀態的,或者引用的 Service 在服務組件配置文件裏配置了可爲 0 個可用狀態的 Service,組件配置纔有可能被激活。在組件實現類中,有兩種策略能夠得到在組件配置文件裏指定的 Target Service,是事件策略和 Lookup 策略。

事件策略

在服務組件激活的過程當中,SCR 必須將組件配置文件裏指定的 Target Service 綁定到組件配置中。在事件策略中,SCR 經過調用組件實現類的一個方法將 Target Service 綁定到組件中,一樣,SCR 經過調用另一個方法來取消綁定,這些方法在組件配置文件中 reference 元素的bind 和 unbind 屬性指定。事件策略主要適用於服務組件所引用的 Target Service 處在動態變化中。如在本文例子中,若是採用事件策略引用姓名查詢服務,在配置文件中聲明和 Component 實現類中引用服務分別如圖示 五、圖示 6 所示:

圖示5:採用事件策略的組件配置文件
圖示5:採用事件策略的組件配置文件
圖示 6:採用事件策略的綁定姓名查詢服務
圖示 6:採用事件策略的綁定姓名查詢服務

Lookup 策略

在 組件實現類中,經過調用 ComponentContext 的 locateService 方法來定位所引用的 Target Service ,該方法的參數是在組件配置文件裏指定的 reference 元素的 name 屬性。如在本文例子中,若是採用 Lookup 策略引用姓名查詢服務,在配置文件中聲明和 Component實現類中引用服務分別如圖示 七、圖示 8 所示:

圖示7:採用 Lookup 策略的組件配置文件
圖示7:採用 Lookup 策略的組件配置文件
圖示 8:採用 Lookup 策略的引用姓名查詢服務
圖示 8:採用 Lookup 策略的引用姓名查詢服務

在 OSGi 服務平臺中,即使 Component 已經綁定所引用的 Target Service,可是因爲服務的動態性,它可能在任什麼時候刻被註冊、替換或者註銷,這些變化可能使服務組件所引用的 Target Service 變成過期的引用,因此,在 Declarative Services 中,當這些狀況發生時,Component必須採起某種策略去處理這些變化。Declarative Services 提供兩種策略,一種是 static 策略 ,另一種是 dynamic 策略 ,默認狀況下 Component 採用的是 static 策略。當採用static 策略時,若是引用的 Target Service 發生了變化,那麼組件配置會被從新裝載並激活。當採用 dynamic 策略時,SCR 在不鈍化組件配置的狀況下能夠改變綁定的 Target Service。此外,Declarative Services 還提供不少功能,如可經過在 Component 的 reference 元素中增長 target 屬性來實現對所引用 Service 進行過濾;如可增長 cardinality 屬性來對引用 Service 的數量進行控制。關於 Declarative Services 更詳細的信息,請讀者參見本文的參考資料。

回頁首

使用 Eclipse 開發服務組件

在 本文中,咱們結合 Equinox 項目關於 Declarative Services 的實現,開發兩個使用服務組件的 Bundle 應用,其中第一個 Bundle 的服務組件的配置文件中聲明註冊了一個姓名查詢服務,用於判斷所給姓名是否在已定義的查詢列表中;第二個 Bundle 應用的服務組件的配置文件中靜態引用了第一個 Bundle 應用服務組件所註冊的姓名查詢服務,若是用戶所給的姓名包含在查詢列表中,將返回正確的信息。最後,將開發的 Bundle 應用部署的 Equinox OSGi 框架中,用戶能夠在 OSGi 控制命令行中輸入命令來查詢關於框架和 Bundle 應用的具體信息。讀者能夠從參考資料中得到本文 Bundle 應用的源代碼。關於 Equinox 項目的詳細信息,請查閱參考資料信息。

(1)首先定義所提供服務的接口,而後 Bundle 應用的服務組件實現這個服務接口。在本例中,定義姓名查詢接口 NameService.java。下面是該接口的源代碼:

NameService Interface 源代碼
package ds.example.service;
/**
 * A simple service interface that defines a name service.
 * A name service simply verifies the existence of a Name.
**/ public interface NameService {
	/**
     * Check for the existence of a Name.
     * @param name the Name to be checked.
     * @return true if the Name is in the list,
     *         false otherwise.
    **/ public boolean checkName(String name);
}

該服務接口很簡單,只包含一個須要實現的方法。一般爲了將服務接口和服務實現相分離,要將該服務接口單獨放在一個包內。

(2 ) 定義 Bundle 描述文件 MANIFEST.MF,Bundle 應用 dsExample 的 MANIFEST.MF 文件以下:

MANIFEST.MF 文件信息
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: DsExample Service Bundle-SymbolicName: dsExample Bundle-Version: 1.0.0 Bundle-Localization: plugin Import-Package: org.osgi.framework;version="1.3.0",
 org.osgi.service.component;version="1.0.0"
Service-Component: OSGI-INF/component.xml Export-Package: ds.example.service

其 中,Service-Component 屬性指定了該 Bundle 應用的服務組件配置文件,在該配置文件中聲明服務而且指定了實現該服務的組件;Export-Package 屬性指定了該 Bundle 輸出的共享包,該屬性可使其餘的 Bundle 應用引用所定義的服務接口。

(3)編輯該 Bundle 應用的服務組件配置文件,正如前面所講 Service 的發佈那樣,該服務組件的配置文件以下:

dsExample Bundle 的組件配置文件
<?xml version="1.0" encoding="UTF-8"?>
<component name="dsExample">
	<implementation 	
		class="example.osgi.NameImpl"/> 
	<service>
	    <provide interface="ds.example.service.NameService"/>
	</service>	 	
</component>

其中,Service 元素定義了所提供服務的接口;Implementation 元素定義了實現該服務接口的組件類名。

(4)實如今服務組件配置文件中指定的服務組件,源代碼以下所示: dsExample 實現組件源代碼

public class NameImpl implements NameService {
	 // The set of names contained in the arrays.
    String[] m_name =
        { "Marry", "John", "David", "Rachel", "Ross" }; protected void activate(ComponentContext context) {
		System.out.println("NameService Component Active,within the bundle
		lifecircle.");
	} public void deactivate(ComponentContext context) throws Exception {
		System.out.println("NameService Component Deactive,within the bundle
		lifecircle.");
	} public boolean checkName(String name) {
		 // This is very inefficient for (int i = 0; i < m_name.length; i++)
        { if (m_name[i].equals(name))
            {
                return true;
            }
        } return false; }
}

該服務組件實現了 NameService 接口,而且在服務組件激活和鈍化時分別打印出相應信息,以便在運行 Bundle 應用時,可以跟蹤 Component 的生命週期。

(5) 建立項目名爲 dsExampleClient 的 Bundle 應用,該應用的服務組件在 OSGi 平臺上查詢並引用 dsExample Bundle 應用已經註冊的姓名查詢服務,而後從標準輸入讀入用戶所輸入的姓名信息,判斷所輸入姓名是否有效。關於 dsExampleClient 應用,讀者可從參考資料中得到完整源代碼,下面只給出該 Bundle 應用的服務組件配置文件:

dsExampleClient Bundle 的組件配置文件
<?xml version="1.0" encoding="UTF-8"?>
<component name="dsExampleClient">
	<implementation 	
		class="exampleclient.osgi.CheckNameClient"/>   
	<reference name="nameservice"
		interface="ds.example.service.NameService"
		cardinality="1..1"
		policy="static"
	/> 	
</component>

其中,reference 元素定義了該組件所引用的服務接口,而且指明該組件採用 static 策略;Implementation 元素指定了實現組件的類。

回頁首

Bundle的部署及運行

在 Eclipse 平臺中,在菜單中選擇 Run-->Run AS-->Equinox FrameWork 來啓動 OSGi 服務平臺。注意在Equinox啓動配置控制檯中的Target Platform 中選擇org.eclipse.equinox.ds選項,該plug-in是Equinox關於Declarative Services的實現,將兩個Bundle應用設置爲取消自動啓動選項。當OSGi Equinox FrameWork啓動後,在OSGi控制命令臺中輸入ss命令,能夠查看OSGi服務平臺中已經安裝的Bundle應用信息及其狀態。如圖9所示,能夠 看到dsExample和dsExampleClient Bundle應用處於Resolved狀態。

圖示9:Bundle狀態查詢
圖示9:Bundle狀態查詢

在 OSGi控制命令臺中利用start命令啓動 dsExample 應用,用ss命令查看啓動後的Bundle應用信息及其狀態,能夠看出 dsExample Bundle 處於Active狀態,可是該Bundle的服務組件並無被激活,若是被激活,將會在OSGi控制命令臺中打印出"NameService Component Active,within the bundle lifecircle."字樣,說明該服務組件爲 Delayed Component 類型,該組件並不會當即被激活,直到該服務組件被請求時, Declarative Services 纔會激活該組件配置,Delayed Component延遲了組件的加載,節省了內存的佔用 ,如圖10所示:

圖示10:啓動dsExample Bundle
圖示10:啓動dsExample Bundle

在OSGi控制命令臺中利用start命令啓動dsExampleClient應用,能夠看出兩個 Bundle的服務組件相繼被激活,如圖11所示:

圖示11:啓動dsExampleClient Bundle
圖示11:啓動dsExampleClient Bundle

在OSGi控制命令臺中利用stop命令中止dsExample應用,能夠看出兩個 Bundle的服務組件相繼被鈍化,如圖12所示:

圖示12:中止dsExample Bundle
圖示12:中止dsExample Bundle

須要注意的是服務組件的生命週期受 Bundle 生命週期的影響,當 Bundle 中止時,那麼Bundle 中全部的服務組件也就中止。

回頁首

總結

Declarative Services 是一個面向服務的組件模型,其目的是更方便地在 OSGi 服務平臺上發佈、查找、綁定服務,對服務進行動態管理。Declarative Services 採用服務組件的延遲加載以及組件生命週期管理的方式來控制對於內存的佔用以及啓動的快速,對 Service 的動態管理,使得系統能夠根據系統運行的狀況作出及時的響應,加強了系統的穩定性和靈活性。項目Gravity 也採用了相似的機制,有興趣的讀者能夠參見參考資料中的詳細信息。

參考資料

相關文章
相關標籤/搜索