诊断 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] 范围内导入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

在这里,我们可以看到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 冲突。这意味着app2spring.orm.hibernate 的导入无法得到满足,因为其其他导入之一与可能提供spring.orm.hibernate 的包的 uses 约束冲突 - 在本例中为spring 包。

诊断此问题的第一步是找出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 包的清单更改为使其可以接受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

现在两个应用程序包都可以启动。不幸的是,还有更微妙的问题在等着我们。根据安装顺序,这组包可能仍然无法一起运行。为了说明这一点,让我们将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 社区中所有即将举行的活动。

查看全部