在接觸 OSGi 時,首先需要學習的概念之一是bundle的概念。 在這篇文章中,我想更仔細地研究一下 bundle 實際上是什麼,以及如何將普通的 jar 轉換為 OSGi bundle。 所以,事不宜遲,
什麼是 bundle?
OSGi 規範將 bundle 描述為“模組化單元”,它“由 Java 類別和其他資源組成,這些資源可以共同向最終使用者提供功能。”。 到目前為止還不錯,但是 bundle 究竟是什麼? 再次引用規範
bundle 是一個 JAR 檔案,它
- 包含 [...] 資源
- 包含一個描述 JAR 檔案內容並提供有關 bundle 資訊的資訊清單檔案
- 可以在 JAR 檔案的 OSGI-OPT 目錄或其子目錄之一中包含可選的文件
簡而言之,bundle = jar + OSGI 資訊 (在 JAR 資訊清單檔案中指定 - META-INF/MANIFEST.MF),不需要額外的檔案或預定義的資料夾佈局。 這意味著從 jar 建立 bundle 所需的全部操作就是將一些條目新增到 JAR 資訊清單。
OSGi 元資料
OSGi 元資料由資訊清單條目表示,這些條目指示 OSGi 框架 bundle 提供或/和需要什麼。 規範指示大約 20 個資訊清單標頭,但我們只會查看您最有可能使用的那些。
Export-Package
顧名思義,此標頭指示匯出哪些套件 (在 bundle 中可用),以便它們可以由其他 bundle 匯入。 只有此標頭指定的套件會被匯出,其餘的將是私有的,並且在包含 bundle 外部不可見。
Import-Package
與 Export-Package 類似,此標頭指示 bundle 匯入的套件。 同樣,只有此標頭指定的套件會被匯入。 預設情況下,匯入的套件是強制性的 - 如果匯入的套件不可用,則匯入 bundle 將無法啟動。
Bundle-SymbolicName
唯一需要的標頭,此條目指定 bundle 的唯一識別碼,基於反向網域名稱慣例 (也由 java 套件使用)。
Bundle-Name
定義此 bundle 的人類可讀名稱,不含空格。 建議設定此標頭,因為它可以提供比
Bundle-SymbolicName 更短、更有意義的 bundle 內容資訊。
Bundle-Activator
BundleActivator 是一個 OSGi 特定介面,允許在 OSGi 框架啟動或停止 bundle 時通知 Java 程式碼。 此標頭的值應包含啟動器類別的完整名稱,該類別應為 public 並且包含沒有任何引數的 public 建構函式。
Bundle-Classpath
當 jar 包含嵌入式程式庫或位於各種資料夾下的類別套件時,此標頭非常方便,方法是擴充預設 bundle 類別路徑 (預期類別直接在 jar 根目錄下可用)。
Bundle-ManifestVersion
這個鮮為人知的標頭指示用於讀取此 bundle 的 OSGi 規範。
1 表示 OSGi 版本 3,而
2 表示 OSGi 版本 4 及更高版本。 由於
1 是預設版本,因此強烈建議指定此標頭,因為 OSGi 版本 4 bundle 在 OSGi 版本 3 下無法正常工作。
以下是一個範例,取自 Spring 2.5.x 核心 bundle 資訊清單,該資訊清單使用上述某些標頭
Bundle-Name: spring-core
Bundle-SymbolicName: org.springframework.bundle.spring.core
Bundle-ManifestVersion: 2
Export-Package:org.springframework.core.task;uses:="org.springframework.core,org.springframework.util";version=2.5.1 org.springframework.core.type;uses:=org.springframework.core.annotation;version=2.5.1[...]
Import-Package:org.apache.commons.logging,edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional[...]
OSGi 元資料上花費的大部分時間可能都花在 Export/Import 套件條目上,因為它們描述了 bundle 之間的關係 (也就是說,在您的模組之間)。 談到套件時,沒有任何隱含內容 - 只有提到的套件會被匯入/匯出,其餘的則不會。 這也適用於子套件:匯出 org.mypackage 將僅匯出此套件,而不匯出其他任何套件 (例如 org.mypackage.util)。 匯入也是如此 - 即使套件在 OSGi 空間中可用,除非特定 bundle 明確匯入該套件,否則該 bundle 看不到該套件。
總而言之,如果 bundle A 匯出套件 org.mypackage 並且 bundle B 想要使用它,則 bundle A 的 META-INF/MANIFEST.MF 應在其 Export-Package 標頭中指定該套件,而 bundle B 應將其包含在其 Import-Package 條目中。
套件注意事項
雖然匯出相當簡單,但匯入稍微複雜一些。 應用程式通常會透過搜尋環境以尋找特定程式庫並僅使用可用的內容來很好地降級,或者程式庫包含使用者不使用的程式碼也很常見。 此類範例包括記錄 (使用 JDK 1.4 或 Log4j)、正則表達式 (Jakarta ORO 或 JDK 1.4+) 或並行公用程式 (JDK 5 中的 java.util 或 JDK 1.4 的 backport-util-concurrent 程式庫)。
在 OSGi 術語中,根據套件的可用性來依賴套件會轉化為可選的套件匯入。 您已經在先前的範例中看到了這樣的套件
```code Import-Package: [...]edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional ```
由於在 OSGi 中,相同類別的多個版本可以存在,因此最佳實務是在匯出和匯入套件時都指定類別套件的版本。 這是透過在每個套件宣告之後新增的 version 屬性來完成的。 OSGi 支援的版本格式為 <major>.<minor>.<micro>.<qualifier>,其中 major、minor 和 micro 是數字,而 qualifier 是字母數字。
版本的意義 完全取決於 bundle 提供者,但是,建議使用流行的編號架構,例如 Apache APR 專案中的 架構,其中
- <major> - 表示一個重要的更新,不保證任何相容性
- <minor> - 表示一個更新,保留與舊次要版本的相容性
- <micro> - 表示一個從使用者角度來看微不足道的更新,它在向前和向後方向上都是完全相容的
- <qualifier> - 是一個使用者定義的字串 - 它沒有被廣泛使用,並且可以為版本號碼提供額外的標籤,例如建置號碼或目標平台,而沒有標準化的意義
預設版本 (如果遺漏屬性) 為 "0.0.0"。
雖然匯出的套件必須指示特定的版本,但匯入器可以使用數學間隔表示法來指示範圍 - 例如
[1.0.4, 2.0) 將符合 1.0.42 及以上版本,直到 2.0 (不包括)。 請注意,指定僅版本而不是間隔將符合所有大於或等於指定版本的套件,即
Import-Package: com.mypackage;version="1.2.3"
等效於
Import-Package: com.mypackage;version="[1.2.3, ∞)"
最後一個提示,請務必在指定版本時始終使用引號,無論它是範圍還是不是。
使用 OSGi 元資料
現在我們已經掌握了一些關於 bundle 是什麼的資訊,讓我們看看我們可以使用哪些工具來 osgi-fying 現有的 jar
手動
不建議使用這種自行手動的方式,因為很容易不小心輸入錯誤或多餘的空格,導致 manifest 檔案無法使用。即使使用智慧型編輯器,manifest 檔案格式本身也可能造成一些問題,因為每行限制為 72 個字元,如果超出限制,可能會產生一些難以理解的問題。手動建立或更新 jar 檔案也不是一個好主意,因為 jar 檔案格式要求 META-INF/MANIFEST.MF 條目必須是封存檔中的第一個條目 - 如果不是,即使它存在於 jar 檔案中,manifest 檔案也不會被讀取。只有在沒有其他替代方案時,才真的建議使用手動方式。
然而,如果真的想要/需要直接處理 manifest 檔案,則應使用可以處理 UNIX/DOS 空格的編輯器,以及適當的 jar 檔案建立工具(例如 JDK 附帶的 jar 工具),以符合所有 MANIFEST 的要求。
Bnd
Bnd 代表 BuNDle 工具,是由 Peter Kriens (OSGi 技術主管) 創建的一個實用的工具,它可以「幫助 […] 建立和診斷 OSGi R4 bundles」。 Bnd 解析 java 類別,以了解可用的和導入的套件,因此它可以建立等效的 OSGi 條目。 Bnd 提供了一系列的指令和選項,可以自定義產生的成品。 Bnd.jar 本身的好處是,它可以從命令列運行,通過專用的 tasks 使用 Ant,或作為 plug-in 整合到 Eclipse 中。
Bnd 可以從類別路徑或 Eclipse 專案中可用的類別建立 jar 檔案,也可以通過添加所需的 OSGi 成品來 osgi-fy 現有的 jar 檔案。此外,它可以列印和驗證有關給定 jar 檔案的 OSGi 資訊,使其成為一個功能強大且易於使用的工具。
第一次使用的使用者,可以使用 Bnd 來查看將添加到 vanilla jar 檔案中的 OSGi manifest。讓我們選擇一個 vanilla jar 檔案,例如 c3p0 (這是一個優秀的連接池函式庫),並發出 print 命令
```code java -jar bnd.jar print c3p0-0.9.1.2.jar ```
輸出相當大,包含幾個部分
- 通用 manifest 資訊
[MANIFEST c3p0-0.9.1.2.jar]
Ant…