診斷 OSGi uses 衝突

工程 | Rob Harrop | 2008 年 11 月 22 日 | ...

在他的 近期部落格文章中,Glyn 介紹了 OSGi 的 "uses" 指令。 在這個部落格中,我想更深入地探討 uses 限制違規的原因,並提供一些診斷應用程式中 uses 問題的技巧。

在大多數範例中,我將使用原始的 Equinox 而不是 dm Server。 這樣做的原因是 uses 限制並非 dm Server 特有,而是與所有 OSGi 使用者相關。 在本部落格的結尾,我將示範一些內建於 dm Server 中的智慧型約束失敗診斷。

相依約束不符

uses 違規最常見的原因是一個或多個相依約束之間的不匹配。 作為範例,請考慮以下三個 manifest 檔

Manifest-Version: 1.0
Bundle-Name: Spring Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: spring
Bundle-Version: 2.5.5
Export-Package: spring.orm.hibernate;version="2.5.5";uses:="eclipselink"
Import-Package: eclipselink;version="[1.0, 2.0)"

Manifest-Version: 1.0
Bundle-Name: EclipseLink 1 Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: eclipselink
Bundle-Version: 1
Export-Package: eclipselink;version="1.0.0"

Manifest-Version: 1.0
Bundle-Name: EclipseLink 2 Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: eclipselink
Bundle-Version: 2
Export-Package: eclipselink;version="2.0.0"

在這裡,您可以看到一個 spring bundle 和兩個 eclipselink bundle。 顯然,這些不是真正的 bundle。 spring bundle 匯入了 eclipselink 套件,範圍為 [1.0, 2.0)。 顯然,只有 eclipselink_1 bundle 可以滿足此約束。 現在,請考慮來自兩個不同應用程式的這些 manifest 檔

Manifest-Version: 1.0
Bundle-Name: App1 Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: app1
Bundle-Version: 1.0.0
Import-Package: spring.orm.hibernate,eclipselink;version="[1.0, 1.0]"

Manifest-Version: 1.0
Bundle-Name: App2 Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: app2
Bundle-Version: 1.0.0
Import-Package: spring.orm.hibernate,eclipselink;version="[2.0, 2.0]"

在這裡,我們可以看到 app1 匯入了 eclipselink,範圍為 [1.0, 1.0],而 app2 匯入了 eclipselink,範圍為 [2.0, 2.0]。 如果我將這些 bundle 安裝到 Equinox 中,然後嘗試啟動應用程式 bundle,主控台會顯示類似以下內容

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
2       RESOLVED    spring_2.5.5
3       RESOLVED    eclipselink_1.0.0
4       RESOLVED    eclipselink_2.0.0
5       ACTIVE      app1_1.0.0
6       INSTALLED   app2_1.0.0

在這裡,我們可以看到 springeclipselink bundle 都已解析。 app1 bundle 已經啟動,但是 app2 bundle 將無法啟動。 為了找出原因,我們可以使用 diag 指令

osgi> diag app2
file:/Users/robharrop/dev/resdiag/uses/app2/bin [6]
  Package uses conflict: Import-Package: spring.orm.hibernate; version="0.0.0"

在這裡,我們可以看到 app2 bundle 無法解析,因為其匯入的 spring.orm.hibernate 存在套件 uses 衝突。 這表示 app2 中對 spring.orm.hibernate 的匯入無法滿足,因為其其他匯入之一與*可以*提供 spring.orm.hibernate 的 bundle 上的 uses 限制衝突 - 在這種情況下,為 spring bundle。

診斷此問題的第一步是找出 spring.orm.hibernate bundle 的可能供應商。 我們從我們的使用案例中知道,唯一的可能供應商是 spring bundle,但是如果您不知道供應商,則可以使用 packages 指令找到它們

osgi> packages spring.orm.hibernate
spring.orm.hibernate; version="2.5.5"<file:/Users/robharrop/dev/resdiag/uses/spring/bin [2]>
  file:/Users/robharrop/dev/resdiag/uses/app1/bin [5] imports

這向我們顯示 spring.orm.hibernate 套件由 bundle 2 匯出。 有了這些知識,我們可以找出 uses 指令中列出了哪些套件,該指令位於 bundle 2spring.orm.hibernate 套件中

osgi> headers 2
Bundle headers:
 Bundle-ManifestVersion = 2
 Bundle-Name = Spring Bundle
 Bundle-SymbolicName = spring
 Bundle-Version = 2.5.5
 Export-Package = spring.orm.hibernate;version="2.5.5";uses:="eclipselink"
 Import-Package = eclipselink;version="[1.0, 2.0)"
 Manifest-Version = 1.0

在這裡,我們可以看到 uses 中唯一的套件是 eclipselink 套件,因此它一定是罪魁禍首。 實際上,我們可以看到 Spring bundle 需要 eclipselink,範圍為 [1.0, 2.0),而 app2 需要 eclipselink,範圍為 [2.0, 2.0] - 這兩個範圍是不相交的,這意味著 app2 *無法*連接到與 spring bundle 相同的 eclipselink 版本。

如果 uses 清單很長,您可以透過找出列出的套件中哪些套件有多個供應商來縮小可能的違規範圍。 必須始終有多個供應商,您才能看到 uses 限制違規。

版本不匹配並不是導致相依約束不符的唯一原因。 由於屬性以及版本,約束可能不匹配。

安裝順序問題

如果我們重新審視先前的範例並更改 spring bundle 的 manifest 檔,以便它可以接受 eclipselink 套件的 2.0 版本,並放寬 app1 上的範圍,以便它可以接受任何超過 1.0 的版本,我們應該能夠解決問題

Manifest-Version: 1.0
Bundle-Name: Spring Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: spring
Bundle-Version: 2.5.5
Export-Package: spring.orm.hibernate;version="2.5.5";uses:="eclipselink"
Import-Package: eclipselink;version="[1.0, 2.0]"

Manifest-Version: 1.0
Bundle-Name: App1 Bundle
Bundle-ManifestVersion: 2
Bundle-SymbolicName: app1
Bundle-Version: 1.0.0
Import-Package: spring.orm.hibernate,eclipselink;version="1.0"

安裝 bundle 並啟動應用程式 bundle 表明此變更帶來了很大的不同

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
1       RESOLVED    spring_2.5.5
2       RESOLVED    eclipselink_1.0.0
3       RESOLVED    eclipselink_2.0.0
4       ACTIVE      app1_1.0.0
5       ACTIVE      app2_1.0.0

現在,兩個應用程式 bundle 都可以啟動。 不幸的是,還有一個更微妙的問題在等待我們。 根據安裝順序,這組 bundle 可能仍然無法一起運行。 為了說明這一點,讓我們將 springeclipselink_1app1 安裝為一個交易,然後啟動 app1

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
1       RESOLVED    spring_2.5.5
2       RESOLVED    eclipselink_1.0.0
3       ACTIVE      app1_1.0.0

現在,讓我們安裝 eclipselink_2app2

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
1       RESOLVED    spring_2.5.5
2       RESOLVED    eclipselink_1.0.0
3       ACTIVE      app1_1.0.0
4       RESOLVED    eclipselink_2.0.0
5       INSTALLED   app2_1.0.0

app2 bundle 將無法啟動。 從 diag 輸出的結果告訴我們原因

osgi> diag app2
file:/Users/robharrop/dev/resdiag/uses/app2/bin [5]
  Package uses conflict: Import-Package: spring.orm.hibernate; version="0.0.0"

uses 限制又回來了。 運行先前章節中識別的診斷步驟在此處無濟於事,因為沒有相依約束不符 - 我們知道這一點,因為第一次這些 bundle 解析得很好。

這裡的問題是解析順序。 這些 bundle 以兩個不同的區塊安裝和解析。 第一個區塊包括 springeclipselink_1app1,第二個區塊包括 eclipselink_2app2。 當第一個區塊被解析時(由於啟動 app1 bundle),spring bundle 會連接到 eclipselink_1 bundle,以匯入 eclipselink 套件。 可以使用主控台確認這一點

osgi> bundle app1
file:/Users/robharrop/dev/resdiag/uses/app1/bin [3]
  Id=3, Status=ACTIVE      Data Root=/opt/springsource-dm-server-1.0.0.RELEASE/lib/configuration/org.eclipse.osgi/bundles/3/data
  No registered services.
  No services in use.
  No exported packages
  Imported packages
    spring.orm.hibernate; version="2.5.5"<file:/Users/robharrop/dev/resdiag/uses/spring/bin [1]>
    eclipselink; version="1.0.0"<file:/Users/robharrop/dev/resdiag/uses/eclipselink1/bin [2]>
  No fragment bundles
  Named class space
    app1; bundle-version="1.0.0"[provided]
  No required bundles

請注意,匯入的套件區段顯示 eclipselink 版本 1.0.0 是從 eclipselink_1 bundle 匯入的。 當安裝第二個區塊時,app2 bundle 無法解析,因為它需要 eclipselink 的版本為 2.0.0,但是 spring 已經連接到版本為 1.0.0eclipselink。 當所有 bundle 作為一個區塊安裝和解析時,OSGi 解析器將嘗試滿足*所有*約束,包括確保可以滿足 spring.orm.hibernate 上的 uses 約束。

為了解決此問題,我們不需要變更我們的 bundle。 相反,我們可以一次性重新安裝 bundle,也可以針對 spring bundle 觸發刷新 - 有效地要求 OSGi 重新運行解析程序。 現在已經安裝了 eclipselink_2 bundle,我們可以預期這會產生不同的結果

osgi> refresh spring

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.4.0.v20080605-1900
1       RESOLVED    spring_2.5.5
2       RESOLVED    eclipselink_1.0.0
3       ACTIVE      app1_1.0.0
4       RESOLVED    eclipselink_2.0.0
5       ACTIVE      app2_1.0.0

osgi> bundle spring
file:/Users/robharrop/dev/resdiag/uses/spring/bin [1]
  Id=1, Status=RESOLVED    Data Root=/opt/springsource-dm-server-1.0.0.RELEASE/lib/configuration/org.eclipse.osgi/bundles/1/data
  No registered services.
  No services in use.
  Exported packages
    spring.orm.hibernate; version="2.5.5"[exported]
  Imported packages
    eclipselink; version="2.0.0"<file:/Users/robharrop/dev/resdiag/uses/eclipselink2/bin [4]>
  No fragment bundles
  Named class space
    spring; bundle-version="2.5.5"[provided]
  No required bundles

請注意,刷新 spring 導致 app2 bundle 解析。 springeclipselink 套件的連接已變更,由 eclipselink_2 bundle 中版本為 2.0.0 的匯出滿足。

dm Server 中的 Uses 限制

當您在 dm Server 中遇到 uses 限制違規時,我們已經嘗試為您執行一些分析步驟,特別是識別可能不匹配的相依約束

Could not satisfy constraints for bundle 'app2' at version '1.0.0'.
 Cannot resolve: app2
  Resolver report:
    Bundle: app2_1.0.0 - Uses Conflict: Import-Package: spring.orm.hibernate; version="0.0.0"
      Possible Supplier: spring_2.5.5 - Export-Package: spring.orm.hibernate; version="2.5.5"
        Possible Conflicts: eclipselink

Uses 限制在企業函式庫中很常見,手動診斷失敗可能是一場真正的噩夢。 特別是,當您有一個匯出的套件,其 uses 子句中列出了 10 個或更多套件時,確定可能的衝突可能非常耗時。 因此,自動化診斷是必須的,我希望不斷改進 dm Server 中的診斷程式碼,以便處理常見錯誤變得微不足道。

在下一個版本中,我們計劃將診斷工具直接構建到我們的 dm Server Eclipse 工具中,以便大多數這些問題將由 dm Server 自動診斷。

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

獲得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

檢視全部