日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用 Equinox 开发 OSGi 应用程序

發(fā)布時間:2025/3/21 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 Equinox 开发 OSGi 应用程序 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

開始之前

關(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)備工作

  • 從附屬資料中下載 Eclipse 3.x 版本,Eclipse 3.2+ 版本已經(jīng)全面支持 OSGi R4 規(guī)范。目前最佳實踐是下載 Eclipse 3.3.2 版本。(下載請見:參考資料)
  • 將 Eclipse 解壓縮到 d:\work\seclipse 目錄,開始我們的 OSGi 之旅。
  • Hello World

    一般情況下,學(xué)習(xí)一門新的技術(shù),程序員都習(xí)慣于首先開發(fā)一個 hello world 應(yīng)用程序,這似乎也是一種“工業(yè)標(biāo)準(zhǔn)”。好的,讓我們開始吧,開發(fā)一個簡單的 OSGi 應(yīng)用程序并不難,步驟如下:

  • 建立一個 plug-in 工程,File > New > Project,選擇?Plug-in development > Plug-in Project

    圖 1. 新建 plug-in 工程

  • 在建立工程的第一個向?qū)?#xff0c;填入工程的名稱:osgi.test.helloworld,使用缺省的工程路徑。注意目標(biāo)平臺的選擇,由于我們的項目是一個通用的 OSGi bundle,所以選擇?equinox?。

    圖 2. 填入工程名及選擇目標(biāo)平臺

  • 在下一個向?qū)Ы缑嬷?#xff0c;填入需要的一些插件信息(注意 Eclipse 中的插件概念基本類似于 OSGi 中的 bundle 的概念),這里需要填入的是 OSGi 的 provider(供應(yīng)商)和 classpath 。如果沒有特別的設(shè)計,一般可以忽略這兩個字段 。最后是關(guān)于 activator 的部分,如果不是一個 fragment bundle 則需要填入,除非您的 bundle 自己實現(xiàn)框架的事件監(jiān)聽,這個似乎也沒有必要。因此,建議使用缺省的設(shè)置,如圖 3:

    圖 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:

    圖 4. 勾掉缺省的選項

  • 完成,基本的插件視圖如圖 5,Eclipse 會在工程名下建立相同路徑的 Java Package,其中包含了 Activator 類,插件的配置信息也都放在 MANIFEST.MF 文件中,將來我們相當(dāng)多的工作都是在其中完成。

    圖 5. 基本的插件視圖

  • 編輯 Activator.java,輸入 hello world 語句,代碼如下:

    清單 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 呢?

  • 執(zhí)行:選擇?Run > Open Run Dialog,進入運行菜單,在 OSGi framework 中右鍵點擊選擇?new?一個新的 OSGi 運行環(huán)境,如圖:

    圖 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è)置屬性,在運行時進行
    控制 bundleInstall安裝
    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-ActivatorBundle 的啟動器
    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)成元素是哪些呢?

  • MANIFEST.MF:描述了 bundle 的所有特征,包括名字、輸出的類或者包,導(dǎo)入的類或者包,版本號等等,具體可以參考 表 2. MANIFEST.MF 文件屬性。
  • 代碼:包括 Activator 類和其它一些接口以及實現(xiàn),這個和普通的 Java 應(yīng)用程序沒有什么特殊的區(qū)別。
  • 資源:當(dāng)然,一個應(yīng)用程序不可能沒有資源文件,比如圖片、properties 文件、XML 文件等等,這些資源可以隨 bundle 一起存在,也可以以 fragment bundle 的方式加入。
  • 啟動級別的定義:可以在啟動前使用命令行參數(shù)指定,也可以在運行中指定,具體的 start level 的解釋,請參考 后面的說明。
  • 框架做了些什么?

    好了,我們已經(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 的 headers 信息,即 MANIFEST.MF 文件;
  • 裝載相關(guān)的類和資源;
  • 解析依賴的包;
  • 調(diào)用其 Activator 的?start?方法,啟動它;
  • 為其提供框架事件、服務(wù)事件等服務(wù);
  • 調(diào)用其 Activator 的?stop?方法,停止它;
  • 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)表

    狀態(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 中的類是如何查找的:

  • 首先,它會找 JRE,這個很明顯,這個實際是通過系統(tǒng)環(huán)境的?JAVA_HOME?中找到的,路徑一般是 JAVA_HOME/lib/rt.jar、tools.jar 和 ext 目錄,endorsed 目錄。
  • 其次,它會找 system bundle 導(dǎo)出的包。
  • 然后,它會找您的 import 的包,這個實際包含兩種:一種是直接通過 require-bundle 的方式全部導(dǎo)入的,還有一種就是前面講的通過 import package 方式導(dǎo)入的包。
  • 查找它的 fragment bundle,如果有的話。
  • 如果還沒有找到,則會找自己的 classpath 路徑(每個 bundle 都有自己的類路徑)。
  • 最后它會嘗試根據(jù) DynamicImport-Package 屬性查找的引用。
  • 啟動級別 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,啟動順序如下:

  • 將啟動級別加一,如果發(fā)現(xiàn)有匹配的 bundle(即 bundle 的啟動級別和目前的啟動級別相等),則啟動這個 bundle;
  • 繼續(xù)第一步,直到發(fā)現(xiàn)已經(jīng)啟動了所有的 bundle,且活動啟動級別和最后的啟動的 bundle 啟動級別相同。
  • 停止順序,也是首先將系統(tǒng)的 start level 設(shè)置為 0:

  • 由于系統(tǒng)當(dāng)前活動啟動級別大于請求的 start level,所以系統(tǒng)首先停止等于當(dāng)前活動啟動級別的 bundle;
  • 將活動啟動級別減一,繼續(xù)第一步,直到發(fā)現(xiàn)活動啟動級別和請求級別相等,都是 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ǔ)上進行。

    我們需要進行下列的步驟:

  • 定義一個服務(wù)接口,并且 export 出去供其它 bundle 使用;
  • 定義一個缺省的服務(wù)實現(xiàn),并且隱藏它的實現(xiàn);
  • Bundle 啟動后,需要將服務(wù)注冊到 Equinox 框架;
  • 從框架查詢這個服務(wù),并且測試可用性。
  • 好的,為了達到上述要求,我們實際操作如下:

  • 定義一個新的包?osgi.test.helloworld.service?,用來存放接口。單獨一個 package 的好處是,您可以僅僅 export 這個 package 給其它 bundle 而隱藏所有的實現(xiàn)類
  • 在上述的包中新建接口?IHello,提供一個簡單的字符串服務(wù),代碼如下:

    清單 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();

    }

  • 再新建一個新的包?osgi.test.helloworld.impl,用來存放實現(xiàn)類。
  • 在上述包中新建?DefaultHelloServiceImpl?類,實現(xiàn)上述接口:

    清單 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";

    ????}

    ?

    ?}

  • 注冊服務(wù),OSGi 框架提供了兩種注冊方式,都是通過?BundleContext?類實現(xiàn)的:
  • registerService(String,Object,Dictionary)?注冊服務(wù)對象?object?到接口名?String?下,可以攜帶一個屬性字典?Dictionary;
  • registerService(String[],Object,Dictionary)?注冊服務(wù)對象?object?到接口名數(shù)組?String[]?下,可以攜帶一個屬性字典?Dictionary,即一個服務(wù)對象可以按照多個接口名字注冊,因為類可以實現(xiàn)多個接口;
  • 我們使用第一種注冊方式,修改?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);

    ?????????

    }

  • 為了讓我們的服務(wù)能夠被其它 bundle 使用,必須在 MANIFEST.MF 中對其進行導(dǎo)出聲明,雙擊 MANIFEST.MF,找到?runtime > exported packages > 點擊 add,如圖,選擇?service?包即可:

    圖 14. 選擇導(dǎo)出的服務(wù)包

  • 另外新建一個類似于 hello world 的 bundle 叫:osgi.test.helloworld2,用于測試osgi.test.helloworld?bundle 提供的服務(wù)的可用性;
  • 添加 import package:在第二個 bundle 的 MANIFEST.MF 文件中,找到?dependencies > Imported packages > Add …,選擇我們剛才 export 出去的?osgi.test.helloworld.service?包:

    圖 15. 選擇剛才 export 出去的 osgi.test.helloworld.service 包

  • 查詢服務(wù):同樣,OSGi 框架提供了兩種查詢服務(wù)的引用?ServiceReference?的方法:
  • getServiceReference(String):根據(jù)接口的名字得到服務(wù)的引用;
  • getServiceReferences(String,String):根據(jù)接口名和另外一個過濾器名字對應(yīng)的過濾器得到服務(wù)的引用;
  • 這里我們使用第一種查詢的方法,在?osgi.test.helloworld2?bundle 的?Activator?的?start?方法加入查詢和測試語句:

    清單 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());

    }

  • 修改運行環(huán)境,因為我們增加了一個 bundle,所以說也需要在運行配置中加入對新的 bundle 的配置信息,如下圖所示:

    圖 16. 加入對新的 bundle 的配置信息

  • 執(zhí)行,得到下列結(jié)果:

    圖 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 框架的事件機制主要核心思想是:

  • 用戶(程序員)可以自己按照接口定義自己的事件類型
  • 用戶可以監(jiān)聽自己關(guān)心的事件或者所有事件
  • 用戶可以將事件同步的或者異步的提交給框架,由框架負(fù)責(zé)同步的或者異步的分發(fā)給監(jiān)聽者
  • 說明:框架提供的事件服務(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. 同步和異步處理演示

  • 在 bundle1 中的 MANIFEST.MF 的 dependency 頁面中定義引入新的包:org.osgi.service.event。
  • 在 bundle1 中的?osgi.test.helloworld.event?包中定義新的類?MyEvent,如下(注意其中的 topic 定義的命名方式):

    清單 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";

    ????}

    ?}

  • 在 bundle1 的?DefaultHelloServiceHandler?類的?getHello?方法中,加入提交事件的部分,這樣 bundle2 在調(diào)用這個服務(wù)的時候,將觸發(fā)一個事件,由于采用了 Post 方式,應(yīng)該是立刻返回的,所以在?postEvent?前后打印了語句進行驗證。

    清單 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";

    }

  • 定義監(jiān)聽者,在 bundle2 中,也引入 osgi 的事件包,然后定義一個新的類:MyEventHandler?類,用來處理事件,這里故意加入了一個延遲,是為了測試異步事件的調(diào)用,實現(xiàn)如下:

    清單 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);

    ?????}

    ?}

  • 注冊監(jiān)聽器,有了事件處理器,還需要注冊到監(jiān)聽器中,這里在 bundle2 的?Activator?類中加入此監(jiān)聽器,也就是調(diào)用?context.registerService?方法注冊這個監(jiān)聽服務(wù),和普通服務(wù)的區(qū)別是要帶一個監(jiān)聽事件類型的 topic,這里列出?Activator?類的?start?方法:

    清單 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());

    }

  • 為了使用框架的事件服務(wù),需要修改運行環(huán)境,加入兩個系統(tǒng) bundle,分別是:
  • org.eclipse.osgi.services
  • org.eclipse.equinox.event
  • 好了一切準(zhǔn)備好了,執(zhí)行:

    圖 20. 執(zhí)行

    可以看到,post?事件后,不等事件真的被處理完成,就返回了,事件處理在另外的線程執(zhí)行,最后才打印處理完成的語句。然后?ss?看一下,目前我們已經(jīng)有五個 bundle 在運行了:

    圖 21. ss 查詢

  • OK,修改代碼以測試同步調(diào)用的情況,我們只需要把提交事件的代碼由?postEvent?修改為sendEvent?即可。其它不變,測試結(jié)果如下:

    圖 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ù),必然有三個步驟

  • 獲取 HttpService,可以像 上述方式 那樣通過 context 的?getService?方法獲得引用;
  • 使用 HttpService 的引用注冊資源或者注冊 Servlet:
  • registerResources:注冊資源,提供本地路徑、虛擬訪問路徑和相關(guān)屬性即可完成注冊,客戶可以通過虛擬訪問路徑 + 資源名稱訪問到資源
  • registerServlet:注冊 Servlet,提供標(biāo)準(zhǔn) Servlet 實例、虛擬訪問路徑、相關(guān)屬性以及 HttpContext(可以為 null)后即可完成注冊,客戶可以直接通過虛擬訪問路徑獲取該 Servlet 的訪問
  • 修改運行環(huán)境,加入支持 http 服務(wù)的 bundle
  • 那么,接下來我們實際操作一下:

  • 首先,在 bundle1 的 src 中建立一個新的 package,名字叫 pages,用來存放一些 HTML 的資源文件,為了提供一個基本的 HTTP 服務(wù),我們需要提供一個 index.html,內(nèi)容如下:

    1

    2

    3

    <html>

    ????<h1>hello osgi http service</h1>

    </html>

  • 第二步,注冊資源服務(wù),首先我們要為 bundle1 加入 HTTP 服務(wù)的 package 引用,即修改 MANIFEST.MF 文件的 dependencies,加入包:org.osgi.service.http;version="1.2.0",然后在?Activator?類的?start?方法中加入 HTTP 資源的注冊:

    清單 10. 加入 HTTP 資源的注冊代碼

    1

    2

    3

    httpService = (HttpService)context.getService

    ????(context.getServiceReference(HttpService.class.getName()));

    httpService.registerResources("/", "/pages", null);

  • 修改運行環(huán)境,在?target platform?的 bundle 列表中加入:org.eclipse.equinox.http?和javax.servlet?這兩個 bundle 保證了 HttpService 的可用性:

    圖 23. 加入 HttpService bundle

  • 運行,然后打開 IE 訪問本機?http://localhost/index.html:

    圖 24. 運行結(jié)果

  • 加入 servlet,首先在 bundle1 建立一個包:osgi.test.hellworld.servlet,建立一個新的類:MyServlet,要從?HttpServlet?基類繼承,實現(xiàn)其?doGet?方法,如下:

    清單 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());

    ????}

    ?}

  • 注冊 servlet,在?Activator?類的?start?方法中加入注冊 servlet 的代碼,如下:

    清單 12. 注冊 servlet 的代碼

    1

    2

    MyServlet ms = new MyServlet();

    httpService.registerServlet("/ms", ms, null, null);

  • 運行,打開 IE 訪問?http://localhost/ms?后得到結(jié)果:

    圖 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)程訪問需要定義的接口和類

    說明:

  • Remote?接口是 J2SE 定義的遠(yuǎn)程對象必須實現(xiàn)的接口;
  • IAppService?接口是 OSGi 服務(wù)接口,繼承了?Remote?接口,即定義方式為:

    1

    public interface IAppService extends Remote

  • AppServiceImpl?實現(xiàn)了?IAppService?接口,此外注意里面的方法都拋出?RemoteException?異常;
  • 實際操作如下:

  • 在 bundle1 的?service?包中加入?IAppService?接口的定義,繼承自?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;

    ?}

  • 把這個接口注冊為 OSGi 標(biāo)準(zhǔn)服務(wù)以及一個 RMI 服務(wù)對象如下:

    注冊為標(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);

    }

  • 在 bundle2 中通過 OSGi 方式使用這個服務(wù):

    清單 16. 使用服務(wù)

    1

    2

    3

    4

    IAppService appService =

    ????(IAppService)context.getService(

    ????????context.getServiceReference(IAppService.class.getName()));

    System.out.println(appService.getAppName());

  • 通過 RMI 方式使用這個服務(wù):

    清單 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());

  • 最終的運行結(jié)果如下:

    圖 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ù)的程序維護平臺

    說明:

  • 通用第三方庫層:這一層包括了常用的第三方庫,例如 apache commons,jfreechart,xml 包等等,這一層需要將這些包全部 export 出去,這樣上層就可以直接通過 require bundle 的方式使用這些包。
  • 業(yè)務(wù)模型定義層:這一層依賴于(require-bundle)通用第三方庫層,定義了應(yīng)用的業(yè)務(wù)模型,例如各種 JavaBeans,也可以在這一層提供額外的應(yīng)用統(tǒng)一配置服務(wù),即為上層應(yīng)用提供配置文件的管理服務(wù)。
  • 業(yè)務(wù)邏輯實現(xiàn)層:這一層依賴于(require-bundle)通用第三方庫層,還依賴于 (import package) 業(yè)務(wù)模型定義層提供的業(yè)務(wù)模型,定義了應(yīng)用的業(yè)務(wù)邏輯,這一層可以細(xì)分為:
  • DAO(Database Access Object)服務(wù)層:即為上層應(yīng)用提供數(shù)據(jù)庫存取服務(wù)的層,其 export 出去的接口全部都是和數(shù)據(jù)庫操作相關(guān)的;
  • Service 層:為 UI 層提供的業(yè)務(wù)邏輯的封裝層,這樣 UI 層只需要執(zhí)行 service 層的接口即可,可以將更多的精力放在 UI 的設(shè)計;
  • 展現(xiàn)維護層:這一層依賴于(require-bundle)通用第三方庫層,和下面各層提供的管理服務(wù)接口,基于 HTTP 服務(wù)的方式提供應(yīng)用的維護,例如配置文件的在線修改、服務(wù)的管理,bundle 的管理,日志的管理,內(nèi)存的管理等等,這些都可以以“ RUNTIME ”的方式展現(xiàn),管理員或者維護人員操作的就是 Equinox 運行環(huán)境。還可以實現(xiàn)大部分的操作不需要重啟 JVM,這一點類似于 JMX。
  • 事件服務(wù):事件服務(wù)層是 OSGi 框架提供的標(biāo)準(zhǔn)服務(wù)之一,為除了通用第三方庫層以外的各層提供事件服務(wù),包括同步、異步的通知各種事件、發(fā)布各種事件等。通過事件服務(wù),可以實現(xiàn)各層之間的聯(lián)動。
  • 這種架構(gòu)的優(yōu)勢在于:

  • 各層只用關(guān)心自己的業(yè)務(wù),例如通用第三方庫層只需要 export,其它事情不用管,它也沒有自己的 Activator 類,業(yè)務(wù)模型定義層只需要關(guān)心業(yè)務(wù)模型,而不必關(guān)心業(yè)務(wù)的流程,業(yè)務(wù)邏輯層中的 DAO 層則只需要關(guān)心數(shù)據(jù)庫操作,service 層則負(fù)責(zé)組合業(yè)務(wù)流程。各司其職,這樣才能精于自己的模塊;
  • 較好的可維護性:最上層的維護展現(xiàn)層,為管理員提供了一個 OSGi 應(yīng)用的管理窗口,提供在線重啟服務(wù)、管理各個 bundle 和服務(wù)的能力,提供了類似于 JMX 的能力;
  • 統(tǒng)一的事件管理框架:為各層定義了統(tǒng)一的事件管理接口,基于 TOPIC 方式的事件監(jiān)聽機制能夠有效的過濾事件,而且提供了異步、同步兩種方式對事件進行處理,可以說有相當(dāng)大的靈活性。
  • 可維護性的考慮

    一般的應(yīng)用架構(gòu)可能都比較多的考慮可靠性、靈活性、可擴展性等,對可維護性卻沒有提供太多的關(guān)注,使用 OSGi 后,將對可維護性提供類似于 JMX 的支持,當(dāng)然這不需要您實現(xiàn) MBEAN,就像上述介紹的架構(gòu)設(shè)計,我們在最上層可以設(shè)計一個基于 HTTP 的維護層,這樣,提供了一個小的 Web 控制臺,供管理員進行維護。

    維護的方面包括:

  • 系統(tǒng)維護
  • Bundle 的管理:包括每個 bundle 的更新、停止、啟動;
  • 服務(wù)的管理:包括運行環(huán)境注冊的服務(wù)的列表、停止、啟動;
  • 系統(tǒng)所有服務(wù)的重啟、停止、啟動;
  • 系統(tǒng)狀態(tài)的監(jiān)控
  • 對各個業(yè)務(wù)層提供的服務(wù)的狀態(tài)進行實時監(jiān)視、統(tǒng)計;
  • 對各個業(yè)務(wù)層提供服務(wù)的狀態(tài)進行控制,通過 OSGi 事件的方式進行通知;
  • 系統(tǒng)日志的管理
  • 對系統(tǒng)中各個層的日志進行統(tǒng)一列表、查看;
  • 對系統(tǒng)中所有操作進行統(tǒng)一日志記錄、管理;
  • 配置管理
  • 對各個業(yè)務(wù)模塊需要的配置文件進行統(tǒng)一展示;
  • 對各個業(yè)務(wù)模塊提供的配置文件提供在線編輯、提交功能;
  • 對修改后的配置文件提供實時上線的功能;
  • 其它
  • 維護系統(tǒng)的登錄、登出;
  • 維護系統(tǒng)自審計;
  • 維護系統(tǒng)權(quán)限控制;
  • 部署 OSGi 應(yīng)用程序

    我們的 bundle 不會只能在 Eclipse 環(huán)境運行,我們需要能夠?qū)?bundle 部署到實際的操作系統(tǒng)中,可能是 Windows/Linux/Unix 等環(huán)境,這要求我們按照下列步驟進行:

  • 發(fā)布 bundle,即將我們的 plug-in 工程發(fā)布為可以執(zhí)行的 Jar 文件或者其它格式;
  • 配置 config.ini,指出 bundle 的運行環(huán)境,啟動順序等;
  • 啟動腳本編寫,編寫能夠運行在各種操作系統(tǒng)的腳本;
  • 發(fā)布 Bundle

    發(fā)布 bundle 的工作其實很簡單,通過 eclipse 平臺即可完成:

  • 選擇 Eclipse 的?plug-in?視圖的?File -> Export,從彈出的窗口中選擇?Deployable plug-ins and fragments

    圖 29. 選擇 Deployable plug-ins and fragments

  • 在下一個窗口中,選擇想要發(fā)布的 bundle,這里我們選擇?osgi.test.helloworldosgi.test.helloworld2?工程,下面的 options 里面選擇“打包為一個 Jar ”,目標(biāo)目錄選擇為?osgi.test.deploy?目錄(在當(dāng)前的 workspace 下面);
  • 選擇確定,發(fā)布后的目錄結(jié)構(gòu)如下圖所示,eclipse 幫我們在部署根目錄下建立了一個新的子目錄 plugins(類似于 eclipse,因為 eclipse 就是基于 OSGi 的):

    圖 30. 發(fā)布后的目錄結(jié)構(gòu)

  • 好了,到這里,發(fā)布工作完成。
  • 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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。