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