在他 最近的博客文章 中,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] 范围内导入 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 捆绑包在 [1.0, 2.0) 范围内需要 eclipselink,而 app2 在 [2.0, 2.0] 范围内需要 eclipselink - 这两个范围是不相交的,这意味着 app2 无法连接到与 spring 捆绑包相同的 eclipselink 版本。
如果 uses 列表很长,您可以通过找出列出的哪些包有多个提供者来缩小可能的冲突范围。您必须始终有多个提供者才能看到 uses 约束冲突。
版本不匹配不是依赖约束不匹配的唯一原因。约束可能不匹配,因为属性以及版本。
安装顺序问题
如果我们重新审视前面的示例,并更改 spring 捆绑包的清单,使其能够接受 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"
安装捆绑包并启动应用程序捆绑包表明此更改产生了重大影响
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
现在这两个应用程序捆绑包都可以启动。不幸的是,还有一个更微妙的问题在等待着我们。根据安装顺序,这组捆绑包可能仍然无法一起运行。为了说明这一点,让我们将 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 捆绑包不会启动。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 约束又回来了。运行上一节中确定的诊断步骤在这里将无济于事,因为没有依赖约束不匹配 - 我们知道这一点是因为第一次这些捆绑包解析得很好。
此处的問題是解析顺序问题。捆绑包被分成两个不同的块进行安装和解析。第一块包括 spring、eclipselink_1 和 app1,第二块包括 eclipselink_2 和 app2。当第一个块解析(作为启动 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.0 的 eclipselink,但 spring 已经连接到版本 1.0.0 的 eclipselink。当所有捆绑包作为一个块安装和解析时,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 捆绑包解析。 spring 中 eclipselink 包的连接已更改,以满足 eclipselink_2 捆绑包中版本 2.0.0 的导出。
在 dm Server 中使用约束
当您在 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 子句中列出了 10 个或更多包时,确定可能的冲突可能非常耗时。因此,自动化诊断是必须的,我希望始终改进 dm Server 中的诊断代码,以便处理常见错误变得微不足道。
在下一个版本中,我们计划将诊断工具直接构建到我们的 dm Server Eclipse 工具中,以便 dm Server 自动诊断大多数这些问题。