診斷 OSGi uses 衝突

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

在他的最近的部落格文章中,Glyn 介紹了 OSGi 的“uses”指令。在本部落格中,我想深入探討 uses 約束違規的原因,並提供一些診斷應用程式中 uses 問題的技巧。

在大多數示例中,我將使用原生的 Equinox 而不是 dm Server。原因在於 uses 約束並非 dm Server 所特有,而是與所有 OSGi 使用者相關。在本部落格的最後,我將演示 dm Server 內建的一些智慧約束失敗診斷功能。

依賴約束不匹配

uses 違規的最常見原因是與一個或多個依賴約束不匹配。以以下三個清單為例:

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 bundles。顯然,這些不是真正的 bundle。 spring bundle 匯入了範圍在 [1.0, 2.0)eclipselink 包。顯然,只有 eclipselink_1 bundle 可以滿足此約束。現在,考慮來自兩個不同應用程式的這些清單:

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 匯入了範圍在 [1.0, 1.0] 的 eclipselink,而 app2 匯入了範圍在 [2.0, 2.0]eclipselink。如果我將這些 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 衝突。這意味著 app2spring.orm.hibernate 的匯入無法滿足,因為它的其他匯入之一與一個 uses 約束衝突,而這個 uses 約束存在於那個可能提供 spring.orm.hibernate 的 bundle 上——在這種情況下,就是 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 匯出。有了這個資訊,我們可以找出 bundle 2spring.orm.hibernate 包的 uses 指令中列出了哪些包:

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 的清單,使其可以接受 2.0 版本的 eclipselink 包,並放寬 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 包時被連線到 eclipselink_1 bundle。這可以透過控制檯確認:

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 已經連線到 eclipselink 的版本 1.0.0。當所有 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 社群所有即將舉行的活動。

檢視全部