在他的最近的部落格文章中,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 套件匯入了 eclipselink 範圍 [1.0, 2.0) 中的套件。 顯然,只有 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] 匯入 eclipselink,而 app2 以範圍 [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
在這裡我們可以看到 spring 和 eclipselink 套件都已解析。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 匯出。 了解了這些知識,我們可以找出 2 套件中 spring.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 的 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 並啟動 app 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
現在兩個 app bundle 都可以啟動了。不幸的是,還有一個更微妙的問題在等著我們。根據安裝順序,這組 bundle 可能仍然無法一起執行。為了說明這一點,讓我們將 spring、eclipselink_1 和 app1 作為一個交易安裝,並啟動 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_2 和 app2。
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 分兩個不同的部分安裝和解析。第一部分包括 spring、eclipselink_1 和 app1,第二部分包括 eclipselink_2 和 app2。當第一部分解析時(由於啟動 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 已經連接到 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 解析。spring 中 eclipselink 套件的連接已變更,並由 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 自動診斷。