本文介紹了一個采用 XML 的插件示例
以便為定義好的擴展點注冊擴展
通過使插件能夠感知 Extention Registry 並提供 OSGi 服務
我們可以完成這一完整的組件退耦操作
插件
擴展點
OSGi
如您所知
Eclipse 的組件架構是基於插件 的
這意味著將一組代碼組件化為單一的組件
然後利用 Eclipse 框架注冊為其組件之一
其他組件可以綁定該組件或調用該組件
擴展點 是插件允許其他插件向公開擴展點的插件提供附加功能的方法
現在利用所有這些插件並將其包裝到受控的運行時
插件可在其中動態進出
並且您可以獲得 OSGi(基本上來說)
示例插件
讓我們從公開擴展點的基本插件開始
這樣可以為同義詞服務注冊新的字符串映射
此項服務允許其他服務注冊一個詞並將其映射到另一個詞(同義詞)
基本擴展包含非常簡單的元素
一個詞
當然還有一個新的同義詞
此插件擴展點的基本結構如表
所示
表
示例插件的元素
我們還要將插件注冊為 OSGi 服務
這意味著它只在顯式執行此操作時被加載
並將可供其他客戶聲明性地使用
為了使用該服務
其他客戶只需了解 Interface 和 OSGi 類名稱
在我們的示例中
我們不會真正調用該服務
因為擴展點是假設的
我們將使用 OSGi API 以告知我們此項服務出入的時間
所以我們可以正確地注冊擴展點
現在這只是一個示例
並且使用針對此概念的擴展點可能不是最好的方法
我們用此基本示例要達到的目的是如何動態注冊新的擴展
同時說明使用 OSGi API 的插件生命周期事件
Mediator 插件
下一個插件是第三方插件
該插件了解已知的服務和擴展點
但不想綁定到此插件
因為後面它將依靠該插件進行運行時解析
這意味著該插件可以駐留在所引用的插件 (pany
SynonymRegistry) 可能不存在的機器上
因為我們現在生活在 OSGi 和動態運行時世界
所以我們想確保插件在不引起運行時故障或錯誤的情況下運行
我們的 mediator 插件將接受同義詞的 XML 文件
並且通過使用提供的擴展點用 SynonymRegistry 插件注冊每個同義詞
清單
用於概念驗證的示例 XML 文件
Synonyms
xml
<?xml version=
encoding=
UTF
?>
<synonyms>
<entry word=
mediator
synonym=
broker
/>
<entry word=
mediator
synonym=
go
between
/>
<entry word=
mediator
synonym=
interceder
/>
<entry word=
mediator
synonym=
intermediary
/>
<synonyms>
Mediator 插件在其 start() 方法中做的第一件事是用 OSGi 服務注冊為一個服務初始化偵聽器
我們要在傳入 start() 方法的 BundleContext 對象上調用 OSGi 服務方法 addServiceListener()
以下代碼展示了一個通過傳入代碼和我們感興趣的服務 ID 調用此 API 的示例
context
addServiceListener( this
pany
SynonymRegistry
)
通過提供過濾器
可以告知 OSGi 服務注冊中心只需通知您指定服務中的狀態更改
在本例中
過濾器只是 SynonymRegistry 類的類名稱
您可能會尋根究底
答案就在啟動序列中
在 OSGi 領域
我們不是總知道另一服務可用的時間
因此我們需要對此進行說明
通過注冊為服務偵聽器
我們可得知服務開始和停止的時間
如果服務不可用
則允許我們緩存同義詞
當服務確實可用時
我們會得到通知並注冊擴展
注冊新擴展
下面我們將講述本文的核心內容
現在我們有了想為其提供動態擴展的數據 (Synonyms
xml) 和已知的擴展點 (pany
SynonymRegistry
Synonym)
由於我們不知道何時初始化插件
也不知道是否初始化 Synonym 插件
所以我們只要在加載插件時嘗試注冊 XML 文件中的條目即可
請記住
這是一個展示概念的示例
不應在生產代碼中這樣實施
通常
我們盡可能多地以惰性方式(延遲或在需要時)執行初始化
Eclipse V
中的新特性是能夠在運行時提供擴展
例如
客戶可以編寫一個包含某個視圖的應用程序
該視圖可以在單擊按鈕時創建一個透視圖
透視圖被添加到擴展注冊表
然後在可用透視圖的列表中顯示
此功能的重要好處之一是它可以減輕插件之間的
硬
依賴性
插件 A 可供在插件 B 中定義的平台使用
無需依賴插件 B
而且
通過將此功能與 OSGi 框架結合
插件可以檢查服務的存在性
如果存在
可從服務中定義的擴展點創建擴展
這在使用面向服務架構的原則同時
促進了真正動態的環境
Eclipse V
中新公開的是 addContribution() 方法
該方法在 IExtensionRegistry 接口中定義
清單
中的代碼展示了可以通過 addContribution() API 添加擴展的方法
addContribution() 方法旨在采用普通 XML 作為第一個參數中的 InputStream
清單
通過 addContribution() API 添加擴展的方法
IExtensionRegistry registry = RegistryFactory
getRegistry( )
Object key = ((ExtensionRegistry) registry)
getTemporaryUserToken( )
ByteArrayInputStream is =
new ByteArrayInputStream( buffer
toString()
getBytes() )
try {
registry
addContribution(is
bundle
null
null
key)
}
finally {
try {
is
close( )
}catch (IOException e) {
}
}
編寫本文的時候
意味著這是一個更改 Eclipse 未來版本的好機會
允許公眾訪問注冊表的用戶標記可以使用此內部 Eclipse 調用獲得
下面的代碼展示了內部 API (getTemporaryUserToken()) 的使用
Object key = ((ExtensionRegistry)registry)
getTemporaryUserToken()
但是
在裡程碑式的下一版本 Eclipse V
版本中
此標記不能公開訪問
為了支持應用程序中的動態擴展
啟動程序必須提供以下針對虛擬機的設置
Declipse
registry
nulltoken=true
此定義現在允許我們將 null 用作 addContribution() API 中的 User Token
現在
我們的代碼看上去類似如下
有關此問題中的 Bugzilla 對話
請參見 Bugzilla bug 清單
清單
getTemporaryUserToken()
…
try {
registry
addContribution(is
bundle
null
null
null)
}
…
上面顯示的緩沖區變量表示實際的 XML 塊
此 XML 是我們可以在 plugin
xml 文件內看到的精確副本
回到我們的 SynonymRegistry 示例
此擴展的 XML 將類似清單
清單
SynonymRegistry 的 XML
<plugin>
<extension point=
pany
synonymregistry
id=
myExtension
>
<synonyms
word=
mediator
synonym=
broker
/>
</extension>
</plugin>
客戶可以考慮創建一個接受以下參數的包裝工廠類
如擴展點 ID
擴展 ID
元素名稱(本例中是同義詞)
實際屬性和資源包 ID
該包裝類將參數格式化為類似上面代碼的 XML 字符串
然後將此 XML 字符串讀入將被傳入到 IExtensionRegistry 接口的 addContribution() 方法的 ByteArrayInputStream 中
只有此方法的其他必需參數是用戶標記和資源包 ID
值得注意的一點是
資源包 ID 應是做出該貢獻的資源包的 ID
不是在其中定義擴展點的資源包的 ID
警告和提示
在 M
(於
年
月
日構建的 Eclipse)中引入的一個特性是
對 addContributions() 的調用是異步調用
這意味著該擴展不可立即使用
因為 Eclipse 啟動了一項執行實際注冊的作業
簡單地說
您必須開始自己的作業並與之同步
以獲得任何類型的同步行為
為了使此項任務更容易
下面給出了三條提示
?創建一項將其本身注冊為一個 RegistryChangeListener 的新作業
?該作業運行時
確保您的作業代碼偵聽 RegistryListener 回調的 isRegistered 集合
?一旦所有注冊完成
即退出您的作業
當然
現在我們必須將調用代碼與生成的作業結合起來
以獲得同步調用
這只有在代碼要求立即使用擴展時才得到保證
希望您的代碼設計為惰性
這樣初始化就變得不重要
結束語
動態擴展的使用可以通過編程方式創建
通過使用 OSGi 框架偵聽服務何時可用(加載或卸載)
動態擴展增強了退耦功能
一起使用這些技術將允許聲明性的貢獻和組件之間
% 退耦
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28797.html