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

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] 的 eclipselinkapp2 匯入了範圍 [2.0, 2.0]eclipselink。如果我將這些捆綁包安裝到 Equinox 中,然後嘗試啟動應用程式捆綁包,控制檯會顯示類似以下內容

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 捆綁包都已解析。app1 捆綁包已啟動,但 app2 捆綁包無法啟動。要找出原因,我們可以使用 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 捆綁包無法解析,因為它匯入的 spring.orm.hibernate 存在包 uses 衝突。這意味著 app2 中對 spring.orm.hibernate 的匯入無法滿足,因為它的其他匯入之一與可能提供 spring.orm.hibernate 的捆綁包(在本例中是 spring 捆綁包)上的 uses 約束髮生衝突。

診斷此問題的第一步是找出 spring.orm.hibernate 捆綁包的可能提供者。從我們的用例中我們知道唯一可能的提供者是 spring 捆綁包,但如果您不知道提供者,可以使用 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 包由捆綁包 2 匯出。有了這些知識,我們可以找出捆綁包 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 捆綁包需要範圍為 [1.0, 2.0)eclipselink,而 app2 需要範圍為 [2.0, 2.0]eclipselink - 這兩個範圍是互斥的,這意味著 app2 無法spring 捆綁包連線到相同版本的 eclipselink

uses 列表很長的情況下,您可以透過找出列出的哪些包具有多個供應商來縮小可能的違規範圍。必須始終有多個供應商才能看到 uses 約束違規。

版本不匹配並非依賴約束不匹配的唯一原因。約束可能由於屬性以及版本而不匹配。

安裝順序問題

如果我們回到之前的示例,並更改 spring 捆綁包的清單,使其能夠接受版本 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"

安裝捆綁包並啟動應用程式捆綁包表明這種改變產生了很大的不同

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

現在兩個應用程式捆綁包都可以啟動。不幸的是,還有一個更微妙的問題等待著我們。根據安裝順序,這組捆綁包可能仍然無法一起執行。為了說明這一點,讓我們將 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 捆綁包無法啟動。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 約束又回來了。執行上一節中確定的診斷步驟將無濟於事,因為沒有依賴約束不匹配——我們知道這一點,因為第一次這些捆綁包解析得很好。

這裡的問題是解析順序。捆綁包分兩個不同的塊安裝和解析。第一個塊包括 springeclipselink_1app1,第二個塊包括 eclipselink_2app2。當第一個塊被解析(由於啟動 app1 捆綁包)時,spring 捆綁包會將其對 eclipselink 包的匯入連線到 eclipselink_1 捆綁包。這可以透過控制檯確認

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 捆綁包匯入的。當第二個塊安裝時,app2 捆綁包無法解析,因為它需要版本 2.0.0eclipselink,但 spring 已經連線到版本 1.0.0eclipselink。當所有捆綁包作為一個塊安裝和解析時,OSGi 解析器將嘗試滿足所有約束,包括確保 spring.orm.hibernate 上的 uses 約束能夠滿足。

要解決此問題,我們無需更改我們的捆綁包。相反,我們可以將捆綁包重新安裝到一個塊中,或者我們可以觸發對 spring 捆綁包的重新整理——實際上是要求 OSGi 重新執行解析過程。現在 eclipselink_2 捆綁包已安裝,我們可以預期會有不同的結果

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 捆綁包解析。springeclipselink 包的連線已更改,由 eclipselink_2 捆綁包中版本 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 社群所有即將舉行的活動。

檢視所有