使用 Equinox 开发 OSGi 应用程序
開始之前
關(guān)于本教程
OSGi 是目前動態(tài)模塊系統(tǒng)的事實上的工業(yè)標(biāo)準(zhǔn),雖然一開始只是作為嵌入式設(shè)備和家庭網(wǎng)關(guān)的框架來使用,但是實際上它適用于任何需要模塊化、面向服務(wù)、面向組件的應(yīng)用程序。而 Equinox 則是的 Eclipse 所使用的 OSGi 框架,是 Eclipse 強大的插件體系的基礎(chǔ),Eclipse 的穩(wěn)定可靠性也為該框架帶來了聲譽。
本教程就將演示如何在 Eclipse 環(huán)境下利用 Equinox 框架進行 OSGi 應(yīng)用開發(fā)。首先解釋了實現(xiàn)上述應(yīng)用程序所必需了解的基本概念和基礎(chǔ)知識,并結(jié)合示例代碼演示 OSGi 開發(fā)的一些重要技術(shù),最后探討了基于 OSGi 應(yīng)用程序一般所采用的架構(gòu),以及如何將 Equinox OSGi 應(yīng)用程序脫離 Eclipse 而部署為一個標(biāo)準(zhǔn)的 Java 應(yīng)用程序。
目標(biāo)
在本教程中,您將學(xué)習(xí):
- OSGi 及框架簡介
- 編寫第一個 OSGi 應(yīng)用程序
- 重要的理論知識
- 開發(fā)一個真實的 OSGi 應(yīng)用程序
- 探討 OSGi 應(yīng)用架構(gòu)
- 部署 OSGi 應(yīng)用程序
先決條件
本教程假設(shè)讀者熟悉基本 Java 語言以及 Eclipse 開發(fā)環(huán)境的使用。
系統(tǒng)需求
本教程假設(shè)您有一個可以工作的 Eclipse 3.x 環(huán)境。如果還沒有,請在?Eclipse 網(wǎng)站?上找到相關(guān)下載的鏈接,以幫助您在自己的系統(tǒng)上操作示例步驟以及運行示例代碼。
OSGi 及框架簡介
OSGi 簡介
OSGi 是目前動態(tài)模塊系統(tǒng)的事實上的工業(yè)標(biāo)準(zhǔn),雖然一開始只是作為嵌入式設(shè)備和家庭網(wǎng)關(guān)的框架來使用,但是實際上它適用于任何需要模塊化、面向服務(wù)、面向組件的應(yīng)用程序。
目前 OSGi 規(guī)范已經(jīng)發(fā)展到第四版(R4), 由 OSGi 聯(lián)合組織(OSGi Alliance)負(fù)責(zé)進行維護管理,相關(guān)的規(guī)范資料也可以從該網(wǎng)站獲得。(參考資料)
OSGi 框架
開發(fā)基于 OSGi 的應(yīng)用程序離不開實現(xiàn)了 OSGi 標(biāo)準(zhǔn)的框架,就好比是基于 J2EE 的開發(fā)離不開應(yīng)用服務(wù)器一樣。目前比較流行的基于 OSGi R4 標(biāo)準(zhǔn)實現(xiàn)的 OSGi 框架有三個:
Equinox:這是大名鼎鼎的 Eclipse 所使用的 OSGi 框架,Eclipse 強大的插件體系就是構(gòu)建在 OSGi bundles 的基礎(chǔ)之上,Eclipse 的穩(wěn)定可靠性為該框架帶來了聲譽,而且由于有 IBM 公司的強力支持,其后續(xù)的開發(fā)和文檔資料也有了一定的保障。一般情況下,我們推薦您使用該框架進行 OSGi 開發(fā)。本教程的后續(xù)部分也將演示如何使用 Equinox 框架來進行 OSGi 應(yīng)用程序的開發(fā)。
Makewave Knopflerfish:這是另外一個比較知名的 OSGi 框架,目前的版本已經(jīng)支持 R4 規(guī)范,其特點在于為應(yīng)用程序的開發(fā)提供了大量的 bundle 。
Apache Flex:由 Apache 基金組織開發(fā)的面向社區(qū)的 OSGi 框架實現(xiàn),提供了標(biāo)準(zhǔn)的服務(wù)和一些有趣的和 OSGi 相關(guān)的服務(wù)實現(xiàn)。
Hello World!編寫第一個 OSGi 應(yīng)用程序
準(zhǔn)備工作
Hello World
一般情況下,學(xué)習(xí)一門新的技術(shù),程序員都習(xí)慣于首先開發(fā)一個 hello world 應(yīng)用程序,這似乎也是一種“工業(yè)標(biāo)準(zhǔn)”。好的,讓我們開始吧,開發(fā)一個簡單的 OSGi 應(yīng)用程序并不難,步驟如下:
圖 1. 新建 plug-in 工程
圖 2. 填入工程名及選擇目標(biāo)平臺
圖 3. 使用缺省設(shè)置
Activator:這是 bundle 啟動時首先調(diào)用的程序入口,相當(dāng)于 Java 模塊中的 main 函數(shù)。不同的是,main 需要通過命令行調(diào)用,而 OSGi 的 Activator 是被動的接受 OSGi 框架的調(diào)用,收到消息后才開始啟動。
最佳實踐:不要在 Activator 中寫太多的啟動代碼,否則會影響 bundle 啟動速度,相關(guān)的服務(wù)啟動可以放到服務(wù)的監(jiān)聽器中。
圖 4. 勾掉缺省的選項
圖 5. 基本的插件視圖
清單 1. 編輯 Activator.java
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package osgi.test.helloworld; ? import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; ? public class Activator implements BundleActivator { ? ?????/* ?????* (non-Javadoc) ?????* @see org.osgi.framework.BundleActivator ?????*???? #start(org.osgi.framework.BundleContext) ?????*/ ?????public void start(BundleContext context) throws Exception { ????????System.out.println("hello world"); ?????} ? ?????/* ?????* (non-Javadoc) ?????* @see org.osgi.framework.BundleActivator ?????*???? #stop(org.osgi.framework.BundleContext) ?????*/ ?????public void stop(BundleContext context) throws Exception { ?????} ?} |
我們可以看到每個 Activator 實際都是實現(xiàn)了?BundleActivator?接口,此接口使 Activator 能夠接受框架的調(diào)用。在框架啟動后,啟動每個 bundle 的時候都會調(diào)用每個 bundle 的 Activator 。
注意:bundle 的 Activator 必須含有無參數(shù)構(gòu)造函數(shù),這樣框架才能使用?Class.newInstance()?方式反射構(gòu)造 bundle 的 Activator 實例。
這里我們在?start?方法中填入了我們希望輸出的 hello world 字符串。那么,怎么才能啟動這個 bundle 呢?
圖 6. 新建 OSGi 運行環(huán)境
在右邊的運行環(huán)境對話框中,輸入運行環(huán)境的名字、start level 和依賴的插件,由于我們目前不需要其它的第三方插件,因此只需要勾上系統(tǒng)的 org.eclipse.osgi 插件,如果不選擇此插件,hello world 將無法運行。如圖 7,只有當(dāng)您點擊了?validate bundles?按鈕 ,并且提示無問題之后,才表明您的運行環(huán)境基本 OK 了。
圖 7. 選擇 org.eclipse.osgi插件
依賴插件的選擇:
圖 8. 依賴插件的選擇
好的,如果您的運行環(huán)境已經(jīng) OK,那么就點擊?Run?吧。
圖 9. 運行 OSGi 項目
恭喜您,成功了!
OSGi 控制臺
OSGi 控制臺對于習(xí)慣開發(fā)普通 Java 應(yīng)用程序的開發(fā)人員來說,還是比較新鮮的。一般來說,通過 OSGi 控制臺,您可以對系統(tǒng)中所有的 bundle 進行生命周期的管理,另外也可以查看系統(tǒng)環(huán)境,啟動、停止整個框架,設(shè)置啟動級別等等操作。如圖 10,鍵入?SS?就可以查看所有 bundle 的狀態(tài):
圖 10. 查看所有 bundle 的狀態(tài)
下面列出了主要的控制臺命令:
表 1. Equinox OSGi 主要的控制臺命令表
| 控制框架 | launch | 啟動框架 |
| shutdown | 停止框架 | |
| close | 關(guān)閉、退出框架 | |
| exit | 立即退出,相當(dāng)于 System.exit | |
| init | 卸載所有 bundle(前提是已經(jīng) shutdown) | |
| setprop | 設(shè)置屬性,在運行時進行 | |
| 控制 bundle | Install | 安裝 |
| uninstall | 卸載 | |
| Start | 啟動 | |
| Stop | 停止 | |
| Refresh | 刷新 | |
| Update | 更新 | |
| 展示狀態(tài) | Status | 展示安裝的 bundle 和注冊的服務(wù) |
| Ss | 展示所有 bundle 的簡單狀態(tài) | |
| Services | 展示注冊服務(wù)的詳細(xì)信息 | |
| Packages | 展示導(dǎo)入、導(dǎo)出包的狀態(tài) | |
| Bundles | 展示所有已經(jīng)安裝的 bundles 的狀態(tài) | |
| Headers | 展示 bundles 的頭信息,即 MANIFEST.MF 中的內(nèi)容 | |
| Log | 展示 LOG 入口信息 | |
| 其它 | Exec | 在另外一個進程中執(zhí)行一個命令(阻塞狀態(tài)) |
| Fork | 和 EXEC 不同的是不會引起阻塞 | |
| Gc | 促使垃圾回收 | |
| Getprop | 得到屬性,或者某個屬性 | |
| 控制啟動級別 | Sl | 得到某個 bundle 或者整個框架的 start level 信息 |
| Setfwsl | 設(shè)置框架的 start level | |
| Setbsl | 設(shè)置 bundle 的 start level | |
| setibsl | 設(shè)置初始化 bundle 的 start level |
MANIFEST.MF
MANIFEST.MF 可能出現(xiàn)在任何包括主類信息的 Jar 包中,一般位于 META-INF 目錄中,所以此文件并不是一個 OSGi 特有的東西,而僅僅是增加了一些屬性,這樣也正好保持了 OSGi 環(huán)境和普通 Java 環(huán)境的一致性,便于在老的系統(tǒng)中部署。表 2 列出此文件中的重要屬性及其含義:
表 2. MANIFEST.MF 文件屬性
| Bundle-Activator | Bundle 的啟動器 |
| Bundle-SymbolicName | 名稱,一般使用類似于 JAVA 包路徑的名字命名 |
| Bundle-Version | 版本,注意不同版本的同名 bundle 可以同時上線部署 |
| Export-Package | 導(dǎo)出的 package 聲明,其它的 bundle 可以直接引用 |
| Import-Package | 導(dǎo)入的 package |
| Eclipse-LazyStart | 是否只有當(dāng)被引用了才啟動 |
| Require-Bundle | 全依賴的 bundle,不推薦 |
| Bundle-ClassPath | 本 bundle 的 class path,可以包含其它一些資源路徑 |
| Bundle-RequiredExecutionEnvironment | 本 bundle 必須的執(zhí)行環(huán)境,例如 jdk 版本聲明 |
重要的理論知識
好的,剛才我們已經(jīng)從頭到尾開發(fā)了一個基于 Equinox 框架的 Hello world 應(yīng)用程序。我們發(fā)現(xiàn)似乎并不是很困難,很多工作 Eclipse 已經(jīng)幫我們做好了,例如 Activator 代碼框架和 MANIFEST.MF 文件,我們也學(xué)會了如何控制 OSGi 的控制臺和編寫 MANIFEST.MF 文件,但是,您真的明白它們是如何運行的么?下面我們將重點介紹一些 OSGi 運行必備的基礎(chǔ)知識。
什么是 bundle?
我們已經(jīng)看到,編寫一個很普通的 Hello world 應(yīng)用,必須首先創(chuàng)建一個 plug-in 工程,然后編輯其 Activator 類的?start?方法,實際我們這樣做的本質(zhì)是為 OSGi 運行環(huán)境添加了一個 bundle,那么一個 bundle 必須的構(gòu)成元素是哪些呢?
框架做了些什么?
好了,我們已經(jīng)明白 bundle 是什么了,也知道如何開發(fā)一個基本的 bundle 了,那么我們還必須要明白,我的 bundle 放在 Equinox 框架中,它對我們的 bundle 做了些什么?
圖 11. Equinox 框架架構(gòu)
實際上,目標(biāo)平臺已經(jīng)為我們準(zhǔn)備了 N 個 bundle,它們提供各種各樣的服務(wù),OSGi 中,這些 bundle 的名字叫 system bundle,就好比精裝修的房子,您只需要拎包入住,不再需要自己鋪地板,裝吊頂了。
我們的 bundle 進入 Equinox 環(huán)境后,OSGi 框架對其做的事情如下:
Bundle 的狀態(tài)變更
OK, 現(xiàn)在我們大概明白了一個 bundle 的定義和其在 OSGi 框架中的生命周期,前面我們看到控制臺可以通過?ss?命令查看所有裝載的 bundle 的狀態(tài),那么 bundle 到底具有哪些狀態(tài),這些狀態(tài)之間是如何變換呢?我們知道了這些狀態(tài)信息,對我們有何益處?
首先,了解一下一個 bundle 到底有哪些狀態(tài):
表 3. Bundle 狀態(tài)表
| INSTALLED | 就是字面意思,表示這個 bundle 已經(jīng)被成功的安裝了 |
| ? | ? |
| RESOLVED | 很常見的一個狀態(tài),表示這個 bundle 已經(jīng)成功的被解析(即所有依賴的類、資源都找到了),通常出現(xiàn)在啟動前或者停止后 |
| STARTING | 字面意思,正在啟動,但是還沒有返回,所以您的 Activator 不要搞的太復(fù)雜 |
| ACTIVE | 活動的,這是我們最希望看到的狀態(tài),通常表示這個 bundle 已經(jīng)啟動成功,但是不意味著您的 bundle 提供的服務(wù)也是 OK 的 |
| STOPPING | 字面意思,正在停止,還沒有返回 |
| UNINSTALLED | 卸載了,狀態(tài)不能再發(fā)生變更了 |
下面請看一張經(jīng)典的 OSGi bundle 變更狀態(tài)的圖:
圖 12. OSGi bundle 變更狀態(tài)圖
Bundle 導(dǎo)入導(dǎo)出 package
OK,到現(xiàn)在為止,似乎一切都是新鮮的,但是您似乎在考慮,OSGi 到底有什么優(yōu)勢,下面介紹一下其中的一個特點,幾乎所有的面向組件的框架都需要這一點來實現(xiàn)其目的:面向服務(wù)、封裝實現(xiàn)。這一點在普通的 Java 應(yīng)用是很難做到的,所有的類都暴露在 classpath 中,人們可以隨意的查看您的實現(xiàn),甚至變更您的實現(xiàn)。這一點,對于希望發(fā)布組件的公司來說是致命的。
圖 13. OSGi bundle 原理
OSGi 很好的解決了這個問題,就像上面的圖顯示的,每個 bundle 都可以有自己公共的部分和隱藏的部分,每個 bundle 也只能看見自己的公共部分、隱藏部分和其它 bundle 的公共部分。
bundle 的 MANIFEST.MF 文件提供了 EXPORT/IMPORT package 的關(guān)鍵字,這樣您可以僅僅 export 出您希望別人看到的包,而隱藏實現(xiàn)的包。并且您可以為它們編上版本號,這樣可以同時發(fā)布不同版本的包。
Bundle class path
這一點比較難理解,一般情況下您不需要關(guān)心這個事情,除非事情出現(xiàn)了問題,您發(fā)現(xiàn)明明這個類就在這里,怎么就是報告 ClassNotFoundException/NoClassDefExcpetion 呢?在您垂頭喪氣、準(zhǔn)備砸掉電腦顯示器之前,請看一下 bundle 中的類是如何查找的:
啟動級別 Start level
在 Equinox 環(huán)境中,我們在配置 hello world 應(yīng)用的時候,看到我們將 framework start level 保持為 4,將 Hello world bundle 的 start level 設(shè)置為 5 。 start level 越大,表示啟動的順序越靠后。在實際的應(yīng)用環(huán)境中,我們的 bundle 互相有一定的依賴關(guān)系,所以在啟動的順序上要有所區(qū)別,好比蓋樓,要從打地基開始。
實際上,OSGi 框架最初的 start level 是 0,啟動順序如下:
停止順序,也是首先將系統(tǒng)的 start level 設(shè)置為 0:
開發(fā)一個真實的 OSGi 應(yīng)用程序
我們不能只停留在 hello world 的層面,雖然那曾經(jīng)對我們很重要 ,但是現(xiàn)實需要我們能夠使用 OSGi 寫出激動人心的應(yīng)用程序,它能夠被客戶接受,被架構(gòu)師認(rèn)可,被程序員肯定。好的,那我們開始吧。下面將會著重介紹一些現(xiàn)實的應(yīng)用程序可能需要的一些 OSGi 應(yīng)用場景。
發(fā)布和使用服務(wù)
由于 OSGi 框架能夠方便的隱藏實現(xiàn)類,所以對外提供接口是很自然的事情,OSGi 框架提供了服務(wù)的注冊和查詢功能。好的,那么我們實際操作一下,就在 Hello world 工程的基礎(chǔ)上進行。
我們需要進行下列的步驟:
好的,為了達到上述要求,我們實際操作如下:
清單 2. IHello
| 1 2 3 4 5 6 7 8 9 | package osgi.test.helloworld.service; ? public interface IHello { ????/** ?????* 得到 hello 信息的接口 . ?????* @return the hello string. ?????*/ ????String getHello(); } |
清單 3. IHello 接口實現(xiàn)
| 1 2 3 4 5 6 7 8 | public class DefaultHelloServiceImpl implements IHello { ? ????@Override ????public String getHello() { ????????return "Hello osgi,service"; ????} ? ?} |
我們使用第一種注冊方式,修改?Activator?類的?start?方法,加入注冊代碼:
清單 4. 加入注冊代碼
| 1 2 3 4 5 6 7 8 9 | public void start(BundleContext context) throws Exception { ????????? ????System.out.println("hello world"); ????context.registerService( ????????IHello.class.getName(), ????????new DefaultHelloServiceImpl(), ????????null); ????????? } |
圖 14. 選擇導(dǎo)出的服務(wù)包
圖 15. 選擇剛才 export 出去的 osgi.test.helloworld.service 包
清單 5. 加入查詢和測試語句
| 1 2 3 4 5 6 7 8 9 10 11 | public void start(BundleContext context) throws Exception { ????System.out.println("hello world2"); ????????? ????/** ????????* Test hello service from bundle1. ????*/ ????IHello hello1 = ????????(IHello) context.getService( ????????context.getServiceReference(IHello.class.getName())); ????????System.out.println(hello1.getHello()); } |
圖 16. 加入對新的 bundle 的配置信息
圖 17. 執(zhí)行結(jié)果
恭喜您,成功了!
使用事件管理服務(wù) EventAdmin
前面講過,OSGi 規(guī)范定義了很多可用的 bundle,您盡管使用它們完成您的工作,而不必另外再發(fā)明輪子,OSGi 框架定義的事件管理服務(wù),類似于 JMS,但是使用上比 JMS 簡單。
OSGi 整個框架都離不開這個服務(wù) ,因為框架里面全都依靠事件機制進行通信,例如 bundle 的啟動、停止,框架的啟動、停止,服務(wù)的注冊、注銷等等等等都是會發(fā)布事件給監(jiān)聽者,同時也在監(jiān)聽其它模塊發(fā)來的自己關(guān)心的事件。 OSGi 框架的事件機制主要核心思想是:
說明:框架提供的事件服務(wù)、事件提供者、事件監(jiān)聽者之間的關(guān)系如下:
圖 18. 事件服務(wù)、事件提供者、事件監(jiān)聽者之間的關(guān)系
事件提供者 Publisher 可以獲取 EventAdmin 服務(wù),通過 sendEvent 同步(postEvent 異步)方式提交事件,EventAdmin 服務(wù)負(fù)責(zé)分發(fā)給相關(guān)的監(jiān)聽者 EventHandler,調(diào)用它們的?handleEvent?方法。
這里要介紹一個新的概念 Topics,其實在 JMS 里面也有用,也就是說一個事件一般都有一個主題,這樣我們的事件接收者才能按照一定的主題進行過濾處理,例如只處理自己關(guān)心的主題的事件,一般情況下主題是用類似于 Java Package 的命名方式命名的。
同步提交(sendEvent)和異步提交(postEvent) 事件的區(qū)別是,同步事件提交后,等框架分發(fā)事件給所有事件接收者之后才返回給事件提交者,而異步事件則一經(jīng)提交就返回了,分發(fā)在另外的線程進行處理。
下面的程序演示了事件的定義、事件的發(fā)布、事件處理,同時還演示了同步和異步處理的效果,以及運行環(huán)境的配置。
(約定?osgi.test.helloworld?為 bundle1,osgi.test.helloworld2?為 bundle2)
圖 19. 同步和異步處理演示
清單 6. 定義新的類 MyEvent
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import java.util.Dictionary; import org.osgi.service.event.Event; ? public class MyEvent extends Event { ????public static final String MY_TOPIC = "osgi/test/helloworld/MyEvent"; ????public MyEvent(String arg0, Dictionary arg1) { ????????super(MY_TOPIC, arg1); ????} ????public MyEvent() { ????????super(MY_TOPIC, null); ????} ? ????public String toString() { ????????return "MyEvent"; ????} ?} |
清單 7. getHello 方法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.event.EventAdmin; ? @Override public String getHello() { ????????? ????//post a event ????ServiceReference ref = ????????context.getServiceReference(EventAdmin.class.getName()); ????if(ref!=null) { ????????eventAdmin = (EventAdmin)context.getService(ref); ????????if(eventAdmin!=null) { ????????????System.out.println("post event started"); ????????????eventAdmin.postEvent(new MyEvent()); ????????????System.out.println("post event returned"); ????????} ????} ????????? ????return "Hello osgi,service"; } |
清單 8. MyEventHandler 類
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; ? public class MyEventHandler implements EventHandler { ? ????@Override ????public void handleEvent(Event event) { ????????System.out.println("handle event started--"+event); ????????try { ????????????Thread.currentThread().sleep(5*1000); ????????} catch (InterruptedException e) { ????????????? ????????} ????????System.out.println("handle event ok--"+event); ?????} ?} |
清單 9. start 方法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Hashtable; ? import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.service.event.EventConstants; import org.osgi.service.event.EventHandler; ? import osgi.test.helloworld.event.MyEvent; import osgi.test.helloworld.service.IAppService; import osgi.test.helloworld.service.IHello; ? public void start(BundleContext context) throws Exception { ????? ????System.out.println("hello world2"); ????????? ????/** ????* 添加事件處理器 . ????*/ ????String[] topics = new String[] {MyEvent.MY_TOPIC}; ????Hashtable<String,String[]> ht = new Hashtable<String,String[]>(); ????ht.put(EventConstants.EVENT_TOPIC, topics); ????EventHandler myHandler = new MyEventHandler(); ????context.registerService( ????????EventHandler.class.getName(), ????????myHandler, ????????ht); ????System.out.println("event handler registered"); ????????? ????/** ????* Test hello service from bundle1. ????*/ ????IHello hello1 = ????????(IHello) context.getService( ????????context.getServiceReference(IHello.class.getName())); ????System.out.println(hello1.getHello()); } |
圖 20. 執(zhí)行
可以看到,post?事件后,不等事件真的被處理完成,就返回了,事件處理在另外的線程執(zhí)行,最后才打印處理完成的語句。然后?ss?看一下,目前我們已經(jīng)有五個 bundle 在運行了:
圖 21. ss 查詢
圖 22. 同步調(diào)用測試結(jié)果
使用 Http 服務(wù) HttpService
OSGi 的 HTTP 服務(wù)為我們提供了展示 OSGi 的另外一個途徑,即我們可以專門提供一個 bundle 用來作為我們應(yīng)用的 UI,當(dāng)然這個還比較簡單,只能提供基本的 HTML 服務(wù)和基本的 Servlet 服務(wù)。如果想提供復(fù)雜的 Jsp/Struts/WebWorks 等等,或者想用現(xiàn)有的 Web 中間件服務(wù)器例如 Tomcat/Resin/WebSphere Application Server 等,都需要另外的途徑來實現(xiàn),目前我提供一些基本的使用 HTTP 服務(wù)的方式。
要使用 HTTP 服務(wù),必然有三個步驟
那么,接下來我們實際操作一下:
| 1 2 3 | <html> ????<h1>hello osgi http service</h1> </html> |
清單 10. 加入 HTTP 資源的注冊代碼
| 1 2 3 | httpService = (HttpService)context.getService ????(context.getServiceReference(HttpService.class.getName())); httpService.registerResources("/", "/pages", null); |
圖 23. 加入 HttpService bundle
圖 24. 運行結(jié)果
清單 11. MyServlet 代碼
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import java.io.IOException; import java.util.Date; ? import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; ? public class MyServlet extends HttpServlet { ????/** ?????* 實現(xiàn)測試 . ?????* @param request the req. ?????* @param response the res. ?????* @throws IOException io exception. ?????*/ ????public void doGet( ????????????HttpServletRequest request, ????????????HttpServletResponse response ????????????) throws IOException { ????????response.getWriter() ????????????.write("hello osgi http servlet.time now is "+new Date()); ????} ?} |
清單 12. 注冊 servlet 的代碼
| 1 2 | MyServlet ms = new MyServlet(); httpService.registerServlet("/ms", ms, null, null); |
圖 25. 運行結(jié)果
分布式部署的實現(xiàn)
分布式部署的實現(xiàn)方式一般可以通過 Web 服務(wù)、RMI 等方式,這里簡單介紹一下基于 RMI 方式的分布式實現(xiàn)。
在 OSGi 環(huán)境中,并沒有直接提供分布式部署的支持,我們可以采用 J2SE 提供的 RMI 方式來實現(xiàn),但是要考慮 OSGi 的因素,即如果您希望您的服務(wù)既可以本地使用,也可以被遠(yuǎn)程訪問,那么您應(yīng)該這樣定義接口和類:
圖 26. 以被遠(yuǎn)程訪問需要定義的接口和類
說明:
| 1 | public interface IAppService extends Remote |
實際操作如下:
清單 13. IAppService 接口定義
| 1 2 3 4 5 6 7 8 | public interface IAppService extends Remote { ????/** ?????* 得到一個遠(yuǎn)程服務(wù)的名稱 . ?????* @return . ?????* @throws RemoteException . ?????*/ ????String getAppName() throws RemoteException; ?} |
注冊為標(biāo)準(zhǔn)服務(wù):
清單 14. 注冊為標(biāo)準(zhǔn)服務(wù)
| 1 2 3 4 5 | IAppService appService = new DefaultAppServiceImpl(context); context.registerService( ????IAppService.class.getName(), ????appService, ????null); |
注冊為遠(yuǎn)程對象:
清單 15. 注冊為遠(yuǎn)程對象
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 啟動 rmi server . * @param service the service. * @throws RemoteException re. */ private void startRmiServer(IAppService service) throws RemoteException { ????if(registry == null) { ????????registry = LocateRegistry.createRegistry(1099); ????} ????// 注冊 appService 遠(yuǎn)程服務(wù) . ????IAppService theService = ????????(IAppService)UnicastRemoteObject.exportObject(service,0); ????registry.rebind("appService", theService); } |
清單 16. 使用服務(wù)
| 1 2 3 4 | IAppService appService = ????(IAppService)context.getService( ????????context.getServiceReference(IAppService.class.getName())); System.out.println(appService.getAppName()); |
清單 17. 通過 RMI 方式使用服務(wù)
| 1 2 3 4 5 6 7 8 9 | String host = "127.0.0.1"; int port = 1099; try { ????Registry registry = LocateRegistry.getRegistry(host,port); ????appServiceStub = (IAppService) registry.lookup("appService"); } catch (Exception e) { ????e.printStackTrace(); } System.out.println("rmi:"+appServiceStub.getAppName()); |
圖 27. 運行結(jié)果
探討 OSGi 應(yīng)用架構(gòu)
設(shè)計思路
到目前為止,我們已經(jīng)涉及到了 OSGi 的諸多方面,那么在實際進行應(yīng)用程序的架構(gòu)設(shè)計的時候我們要考慮哪些因素呢,這一節(jié)我們詳細(xì)討論一下這個問題。
應(yīng)用架構(gòu)的設(shè)計應(yīng)該充分考慮到可靠性、可擴展性、可維護性等因素,使用了 OSGi 框架后,我們可以更加容易的實現(xiàn)系統(tǒng)分層,組件化的設(shè)計方式。通過使用 HTTP 服務(wù)我們可以設(shè)計出一個基于 HTTP 服務(wù)的程序維護平臺。架構(gòu)如下:
圖 28. 基于 HTTP 服務(wù)的程序維護平臺
說明:
這種架構(gòu)的優(yōu)勢在于:
可維護性的考慮
一般的應(yīng)用架構(gòu)可能都比較多的考慮可靠性、靈活性、可擴展性等,對可維護性卻沒有提供太多的關(guān)注,使用 OSGi 后,將對可維護性提供類似于 JMX 的支持,當(dāng)然這不需要您實現(xiàn) MBEAN,就像上述介紹的架構(gòu)設(shè)計,我們在最上層可以設(shè)計一個基于 HTTP 的維護層,這樣,提供了一個小的 Web 控制臺,供管理員進行維護。
維護的方面包括:
部署 OSGi 應(yīng)用程序
我們的 bundle 不會只能在 Eclipse 環(huán)境運行,我們需要能夠?qū)?bundle 部署到實際的操作系統(tǒng)中,可能是 Windows/Linux/Unix 等環(huán)境,這要求我們按照下列步驟進行:
發(fā)布 Bundle
發(fā)布 bundle 的工作其實很簡單,通過 eclipse 平臺即可完成:
圖 29. 選擇 Deployable plug-ins and fragments
圖 30. 發(fā)布后的目錄結(jié)構(gòu)
Config.ini
為了讓我們的 Jar 文件跑起來,需要 OSGi 的運行環(huán)境支持,所以我們需要拷貝一些 system bundle 到 plugins 目錄中,包括:
圖 31. OSGi 的運行環(huán)境支持
然后,把 eclipse 目錄的 org.eclipse.osgi_3.3.2.R33x_v20080105 文件拷貝到 osgi.test.deploy 根目錄,重命名為 equinox.jar 文件。
在 osgi.test.deploy 目錄新建子目錄 configuration,新建一個文本文件 config.ini,用來配置 bundle 的啟動環(huán)境,配置如下:
圖 32. config.ini 配置文件
注意最后兩個 bundle 的啟動順序配置格式為:bundle@start_leve:start。
好了,config.ini 也已經(jīng)準(zhǔn)備好了。
啟動腳本
下面進行啟動腳本編寫,這個和普通的 Java 程序沒有什么大的區(qū)別,都是調(diào)用 Java 程序執(zhí)行一個 jar 文件,關(guān)鍵是其中的一些參數(shù)定義:
圖 33. 啟動腳本
注意?1/2/3/117/118?參數(shù)都是 OSGi 環(huán)境特有的。
運行
雙擊 run.bat,可以看到如下結(jié)果:
圖 34. 運行結(jié)果
總結(jié)
通過閱讀本文您應(yīng)該已經(jīng)掌握了使用 Equinox 開發(fā)基于 OSGi 的應(yīng)用程序的方法,了解了其關(guān)鍵的理論知識,還學(xué)習(xí)了如何開發(fā)分層的, 模塊化的、分布式的應(yīng)用程序,掌握了在 Windows 平臺部署基于 Equinox 平臺的 OSGi 應(yīng)用程序的方法。總體上看,OSGi 能夠有效的降低模塊 之間的耦合程度,將軟件設(shè)計的開閉原則(Open-Close Principle)提高到一個新的水平,另外 OSGi 也為系統(tǒng)架構(gòu)設(shè)計提供了更大的靈活性,使得我們開發(fā)出像 Eclipse 那樣插件化的平臺系統(tǒng)不再遙不可及。
相關(guān)主題
- Eclipse.org:獲得有關(guān) Eclipse 的更多詳細(xì)資料。
- Equinox:獲得有關(guān) Equinox 框架的更多詳細(xì)資料。
- OSGi Alliance Service Platform:了解更多關(guān)于 OSGi 的信息,包括 OSGi Release 4 規(guī)范等信息。
- Help – Eclipse SDK:獲得在 Eclipse 下進行開發(fā)的詳細(xì)幫助文檔。
- “Eclipse 平臺入門 -- 使用 Eclipse 插件來編輯、編譯和調(diào)試應(yīng)用程序”(developerWorks,2004 年 2 月):本文為您提供關(guān)于 Eclipse 平臺的概述,包括其起源和體系結(jié)構(gòu)。
- “了解 Eclipse 插件如何使用 OSGi”(developerWorks,2006 年 9 月):闡明了 Eclipse 與 OSGi 的關(guān)系,還解釋了 OSGi manifest.mf 文件選項以及通過 Eclipse 提供的添加項。
- “基于 OSGi 的面向服務(wù)的組件編程”(developerWorks,2007 年 8 月):本文介紹了基于 OSGi 開發(fā)一個應(yīng)用程序的過程,讀者可以學(xué)習(xí)如何基于 OSGi 開發(fā)自己的應(yīng)用。
- “探索 OSGi 框架的組件運行機制”(developerWorks,2008 年 7 月):本文介紹了 OSGi 框架中的組件(Bundle)的運行機制,并結(jié)合實際示例加以說明。
- developerWorks Eclipse 技術(shù)資源中心:這里匯集了大量和 Eclipse 開發(fā)平臺相關(guān)的技術(shù)文章和教程。
- developerWorks Java 技術(shù)專區(qū):這里有數(shù)百篇關(guān)于 Java 編程方方面面的文章。
- 下載?Eclipse。
- 下載?Equinox OSGi 框架。
from:https://www.ibm.com/developerworks/cn/education/opensource/os-eclipse-osgi/index.html?
總結(jié)
以上是生活随笔為你收集整理的使用 Equinox 开发 OSGi 应用程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JBoss Modules – Modu
- 下一篇: 探索 OSGi 框架的组件运行机制