《深入理解OSGi:Equinox原理、应用与最佳实践》一2.4 Bundle的组织与依赖

简介: 本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.4节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.4 Bundle的组织与依赖

既然是以模块化方式开发一个系统,那么必不可少的步骤是根据业务和技术的需要,将系统划分为多个模块,通过这些模块互相协作完成系统的功能。系统中绝大部分模块都不是孤立的,通常会依赖其他模块所导出的某些Package,又会被另外一些模块所依赖。这种依赖关系在元数据配置中简单体现为Import-Package、Export-Package和Require-Bundle标记的配置,使用起来并不算复杂,但是各个OSGi框架的实现者都要花费大量心思在组织Bundle与管理依赖上,如何查找最合适的Bundle、如何处理循环依赖关系、如何导入导出Bundle中的类和资源等一系列问题都是需要OSGi框架去解决的。

2.4.1 导出和导入Package

Export-Package用于声明Bundle要导出哪些Package,Import-Package用于声明Bundle需要导入哪些Package。这两个标记最简单的方式是直接跟随导入或导出的Package名称,如果导入或导出多个Package,则使用逗号分隔,如下所示:
Export-Package: org.osgi.service.io, org.osgi.service.web
Import-Package: org.osgi.service.io, org.osgi.service.web
这种写法是OSGi中导入导出Package的基本用法,也是最常见用法,已经能满足相当多的应用场景,但是在开发过程中总会遇见各种不同的需求,要根据特定规则去选择适合的Package。下面对OSGi规范中定义的几种对导入导出进行筛选的过滤方式进行介绍。
1.根据类名过滤
如果仅在Package层次上控制导出的粒度不够精细,无法满足应用需求,那么可以使用附加参数include和exclude进一步描述要导出Package中哪一部分内容。比如,只希望导出org.osgi.service.io包中命名以“IO”开头的接口,不导出实现类(假设实现类都以Impl结尾),那么可以这样写:
Export-Package: org.osgi.service.io;include="IO"; exclude:="Impl"
include和exclude参数的具体使用方法如下:
附加参数include用于列举需要导出Package中哪些类,类名使用逗号分隔,可以使用通配符。如果加入这个参数,那么只有符合规则的类才会被导出。
附加参数exclude用于列举禁止导出Package中哪些类,类名使用逗号分隔,可以使用通配符。如果加入这个参数,那么只要符合规则的类就不会被导出。
include和exclude的限制是在导出时处理的,导入时无需对应用做任何特殊声明,Import-Package标记也无法与这两个参数搭配使用。例如与前面配对的导入声明依然为:
Import-Package: org.osgi.service.io
2.根据版本过滤
在OSGi系统中,同一个名称的Package可能存在多个不同版本。假设Bundle C开发时引入了Spring 2.0版的Package,并且使用了某些只在这个版本私有的特征,而Bundle D开发时使用的是Spring 3.0版的Package,那么从这个系统中导出Spring的Bundle就必须明确指明Spring的版本号,以便导入时区分。示例如下:
Bundle A(导出Spring 2.0):
Export-Package: org.springframework.core; version="2.0.0"
Bundle B(导出Spring 3.0):
Export-Package: org.springframework.core; version="3.0.0"
对应的,导入时也要指明版本,准确地说应指明某个版本范围,例如:
Bundle C(导入Spring 2.x版):
Import-Package: org.springframework.core; version="[2.0.0,2.9.9]"
Bundle D(导入Spring 3.0以上版本):
Import-Package: org.springframework.core; version="3.0.0"
这里要注意我们导入的是“版本范围”而不是某个具体的“版本”,例如示例中Bundle D的Import-Package写法,只声明了version="3.0.0"的含义并不是“只导入”3.0.0版本的Package,而是导入3.0.0或以上版本的Package,因为这更符合一般的开发使用场景。如果需要指定只导入3.0.0版的Package,需要这样写:
Bundle E(只导入Spring 3.0版本):
Import-Package: org.springframework.core; version="[3.0.0,3.0.0]"
version参数的具体使用方法是:在导出Package时,此参数声明导出Package的版本号,如果不设置,默认值为0.0.0;在导入Package时,此参数声明要导入Package的版本范围,如果不设置,默认值为[0.0.0, ∞)。在声明版本范围时,方括号“[”和“]”表示“范围包含此数值”,圆括号“(”和“)”表示“范围不含此数值”。
在OSGi R3.x及之前的版本中,version标记原本叫做specification-version,在R4.x规范中新增了version标记,但依然保留了specification-version这个别名,但是已将它声明为Deprecated,如果同时设置了version和specification-version的值,那么这两个值必须相等。
3.根据提供者过滤
OSGi还允许开发人员根据Bundle名称、版本等信息来过滤Package,这种过滤方式在规范中被称为“选择提供者(Provider Selection)”。由开发人员明确指明导入的Package必须来自某个提供者的做法在实际开发之中是很少见的,它会增加系统的兼容性风险和人为的不确定因素。就如同我们组装电脑选择显示卡一样,理性的选择方式是根据性能需求确定显示芯片、显存大小和位宽等参数,再来选择合适的产品,而不是明确要求必须选择某公司出产的某型号产品。
根据提供者过滤一般使用在测试Bundle的时候,Import-Package标记提供了两个附加参数bundle-symbolic-name和bundle-version来完成这项功能,它们分别用于对Bundle的名称和版本进行过滤,示例如下:
Bundle A:
Bundle-SymbolicName: BundleA
Import-Package: com.acme.foo; bundle-symbolic-name="BundleB";
bundle-version="[1.4.1,2.0.0)"

Bundle B
Bundle-SymbolicName: BundleB
Bundle-Version: 1.4.1
Export-Package: com.acme.foo
上面配置指明一定要是来自于名称为“BundleB”并且版本在1.4.1至2.0.0之间的com.acme.foo包才会被选择。
bundle-symbolic-name和bundle-version参数的具体使用方法如下。
附加参数bundle-symbolic-name:参数值为某个Bundle的名称,只有符合该名称的Bundle所导出的Package才会被导入。
附加参数bundle-version:参数值为导入Bundle(注意不是Package)的版本范围,只有版本在该范围之内的Bundle所导出的Package才会被导入。
4.根据属性过滤
导入和导出的Package除了使用include、exclude对类名进行过滤,使用version对版本进行过滤和使用bundle-symbolic-name、bundle-version对提供者信息进行过滤外,还有第四种方式:使用自定义的扩展属性进行过滤。包名、类名和版本这几项信息都是很客观的数据,在代码开发完成那一刻就已确定下来,不可能随意改变。而自定义的扩展属性可以满足某些根据开发人员自己加入的属性进行过滤的需求。示例如下:
Export-Package: org.osgi.simple;filter="true";
在导出org.osgi.simple时加入了自定义属性filter,它的值为“true”(属性值按照字符串处理),那下面三句Import-Package中,只有第Bundle B、C可以成功导入前面发布的org.osgi.simple包,而Bundle A中由于自定义属性冲突导致而导致匹配失败,示例如下:
Bundle A:
Import-Package: org.osgi.simple; filter="false"

Bundle B:
Import-Package: org.osgi.simple; filter="true"

Bundle C
Import-Package: org.osgi.simple;
注意,Bundle C虽然没有声明自定义属性filter,但在默认情况下这并不会产生匹配冲突。如果要改变这种情况,可以使用mandatory附加参数,强制要求必须存在扩展属性才能成功匹配,示例如下:
Export-Package: org.osgi.simple; filter="true";mandatory:="filter"
这样,在下面两句Import-Package中,只有第一句能成功导入前面发布的“org.osgi.simple”包,因为它发布时filter属性被声明为“必须”的,示例如下:
Bundle A:
Import-Package: org.osgi.simple; filter="true"

Bundle B:
Import-Package: org.osgi.simple
如果没有在mandatory中指定属性名称,那这种属性被默认为“optional”,即可选的,在导入时没有声明这个属性也不影响正常导入。
mandatory参数的具体使用方法:只适用于Export-Package,用于声明哪些属性是必选的,只有导入Package时提供了必选的属性,才能正确匹配到导出的Package。多个属性用逗号分隔,用双引号包裹。
5.可选导入与动态导入
在大多数情况下,导入某个Package,就说明当前这个Bundle的正常运行是必须依赖导入的Package的,比如一个基于Spring开发的程序,没有导入Spring的Package就肯定无法运行。但还有另外一些场景,导入某个Package是为了实现一些不影响Bundle正常运行的附加功能,比如为某个英文软件开发了一个实现中文语言支持的插件,不导入这样的Package也不应当影响整个系统正常运行,只不过软件仍以英文形式显示而已。
Import-Package标记也可以很好地支持上述需求,它有一个名为resolution的附加参数,用于说明一个依赖的Package是否是必需的。示例如下:
Bundle A:
Import-Package: org.osgi.simple;resolution:="mandatory"

Bundle B:
Import-Package: org.osgi.simple

Bundle C:
Import-Package: org.osgi.simple; resolution:="optional"
在上面例子中,Bundle A和Bundle B的Import-Package是等价的,因为resolution的默认值就是mandatory。在这个设置下,如果没有任何Bundle导出org.osgi.simple包,那么Bundle A 和Bundle B将会解析失败。而Bundle C不会,因为它明确指出了这个Package的导入是可选的(resolution:="optional")。
resolution参数的具体使用方法是这样的:附加参数resolution只适用于Import-Package标记,用于确定某个Package是可选的还是必须的。
还有一种场景,可能某个被导入的Package确实是必需的,但是提供这个Package的Bundle并不会在系统启动时就被安装。在这种情况下,只有真正使用到这个Package的类时,才会去导入这个Package,而不是在启动时就查找是否有提供Package的Bundle存在。
DynamicImport-Package标记可以处理这样的需求,这个标记的语义和Import-Package标记很类似,不同点在于DynamicImport-Package标记不在Bundle的解析阶段进行处理,无论它要求导入的Package是否存在,都不会影响Bundle的解析,示例如下:
Bundle A:
Import-Package: org.osgi.simple

Bundle B:
DynamicImport-Package: org.osgi.simple
如果没有任何Bundle提供org.osgi.simple包,那么Bundle A将无法解析,而Bundle B不受影响;但是如果真正使用到org.osgi.simple中的类时还是没有任何Bundle可提供,那么Bundle B依然要抛出ClassNotFoundException异常。
DynamicImport-Package也可是使用version附加参数来过滤导入Package的版本,在导入Package时声明属性,以便符合Package导出时mandatory参数中声明必须存在属性。
与Import-Package有所不同的是,DynamicImport-Package可以使用通配符,举一个极端的例子:
DynamicImport-Package: *
如果某个Bundle的元数据信息中有上面这行定义,那么它就成了一个“管理员级别”的Bundle,它可以访问整个系统中所有被导出过的Package。这样做很方便,却违背了OSGi中提倡的封装与按需使用的原则,会带来许多遗患,因此这并不是一种好的使用方式。
动态导入和可选导入实现的功能有些类似,它们的共同特征是在Bundle解析期间即使找不到要导入的依赖,也不会导致解析失败。它们的区别是,动态导入每次加载包中的类都会尝试去查找动态导入包,而可选导入包只有在Bundle解析时才进行连接尝试。
6.导出Package的依赖限制
如果导出Package的全部依赖都集中在一个Bundle之中,那么这个Package的导出是完全不受限制的。但是如果要导出的Package中有某些类还依赖于其他Bundle所提供的类,比如从其他Bundle所导出的Package中的类继承,或者其他Bundle的类出现在方法的声明中,在这种情况下,要导出这个Package就会受到依赖限制,必须先保证依赖的Package是可用的,才能保证导出的Package是可用的。
这种Package之间的关系可以通过在Export-Package标记中的uses附加参数来描述。例如:包org.osgi.service.http使用了包javax.servlet.http中的API,因此这两个包存在依赖关系。在导出org.osgi.service.http的Export-Package标记中就应当包含值为javax.servlet的uses参数,如下所示:
Export-Package: org.osgi.service.http;uses:="javax.servlet.http"
当一个系统中同时存在不同版本的Package时,uses参数对于协调依赖关系很有用。比如上面例子中的javax.servlet.http包同时存在2.4和2.1两个版本,而Bundle A导入的是2.4版,如下所示:
Bundle A:
Export-Package: org.osgi.service.http;uses:="javax.servlet.http"
Import-Package:javax.servlet.http; version="2.4"
对于任意一个Bundle,只要导入Bunlde A中发布org.osgi.service.http包,同时又导入了javax.servlet.http包,即使在导入的时候不明确指明其版本,OSGi实现也必须保证它只会使用2.4版的javax.servlet.http,如下所示:
Bundle B:
Import-Package:org.osgi.service.http // 由Bundle A提供
,javax.servlet.http // 这里默认会导入2.4版的Package
uses参数的具体使用方法是:附加参数uses只适用于Export-Package标记,用于说明导出Package的依赖关系。如果同时依赖多个Package,使用逗号分隔,并用双引号包裹。
当OSGi容器中的Bundle不断增加,依赖关系逐渐变得复杂时,uses参数就是协调依赖的必要手段,也是配置OSGi依赖的难点之一。在2.4.2节中我们还会对如何使用uses参数协调Package依赖约束进行更详细介绍。
7.导入整个Bundle
前面几项介绍的都是Package级别的导入、导出和依赖,OSGi还可以支持Bundle级别的依赖关系。我们可以使用Require-Bundle标记来声明要导入这个Bundle,如果成功导入了这个Bundle,那就意味着导入了这个Bundle中所有声明为导出的Package。
Require-Bundle标记后面应跟随一个或多个其他Bundle的名称(由Bundle-SymbolicName定义的名称),多个名称之间使用逗号分隔,如下所示:
Require-Bundle:BundleA、BundleB、BundleC
如果导入了某个Bundle,就可以重复导出该Bundle中已导出过的Package,就如同导出自己的Package一样,例子如下所示:
Bundle A:
Require-Bundle: BundleB
Export-Package: p

Bundle B:
Export-Package: p;partial=true;mandatory:=partial
这时如果有Bundle C要导入Package p,而又没有声明属性partial,那它将会从Bundle A中导入Package p。实际上Bundle A只承担了转发的工作,真正的Package p在Bundle B之中。这种情况属于拆分包的一个特例,应尽可能避免。
注意 拆分包(Split Packages)是指OSGi容器中有两个或两个以上的Bundle导出了相同名称的Package。容器存在拆分包会令包导入过程变得复杂,因为只带有包名的Import-Package标识无法保证能正确导入到所需的Package。必须通过过滤或者使用Require-Bundle才能导入正确的Package。
与Import-Package类似,Require-Bundle标记也有附加参数bundle-version和resolution。bundle-version用于过滤导入Bundle的版本,默认值为[0.0.0,∞),即不限制版本范围。resolution用于确定导入的Bundle是否是必需的,可选值为mandatory和optional,默认值为mandatory,含义与Import-Package标记的resolution参数相同,这里就不再详细举例介绍了。
在默认设置下,导入了某个Bundle,仅表示在本Bundle中可以访问被导入Bundle中声明导出的Package,除非明确用Export-Package声明过,否则这些Package在本Bundle中默认不会再次被导出。如果有必要,可以使用Import-Package标记的visibility附加参数来改变这种行为,示例如下:
Bundle A
Require-Bundle: BundleB;visibility:=reexport

Bundle B
Export-Package: org.osgi.service.http
在上面例子中由于明确设置了visibility的值为reexport,Bundle A中就会重复导出Bundle B的声明导出的包,即org.osgi.service.http。
visibility参数的具体使用方法是:附加参数visibility仅用于Require-Bundle标记,描述来自被导入Bundle中的Package是否要在导入Bundle中重新导出。默认值为private,代表不会在导入Bundle中重新导出。如果值为reexport,则说明需要重新导出。
在元数据信息中可以同时使用Import-Package和Require-Bundle标记来获取Bundle所需的依赖包,但是如果某个依赖的Package同时在Import-Package列表和Require-Bundle的Bundle中存在,那么OSGi实现框架必须保证要以Import-Package列表中的包优先。
使用Require-Bundle有时候确实会获得一些便利,但从长远来看,依赖的粒度越小越好。依靠Require-Bundle来处理依赖关系并非一种好的开发实践,甚至可能会带来一些令人头痛的问题,我们将在2.4.2节中通过实际例子来介绍Require-Bundle的缺陷。
2.4.2 约束规则与示例
导入和导出Package并不总是一帆风顺的,随着系统复杂性的增加,模块数量不断变多,依赖关系随之变得越来越错综复杂,尤其有拆分包或在多个不同版本的Package同时存在和出现循环依赖等情况时,理解依赖的约束规则就很重要了,本节将介绍这方面的内容。
1.图示
在本书后续章节中,我们将会以图示来表示各个Bundle、Package以及它们之间的依赖关系,如图2-6所示。这些图示最初定义在OSGi规范之中(除了最后一个“解析失败的Bundle”,这个图示是笔者为了讲解方便自行定义的),因此在本书之外的其他OSGi文档也会遇到类似的图示。
下面是一个如何使用图示来表示Bundle、Package及其依赖关系的例子,假设存在A、B、C三个Bundle,其定义如下所示:

图2-6 OSGi标准图示与标记
Bundle A:
Import-Package: p; version="[1,2)"
Export-Package: q; version=2.2.2; uses:=p
Require-Bundle: C

Bundle B
Export-Package: p; version=1.5.1

Bundle C
Export-Package: r
这3个Bundle以及它们之间的依赖关系可以使用图2-7来表示。

图2-7 图示示例
2.多版本Package依赖选择
前面介绍过如何使用Export-Package标记uses附加参数来协调在同一个OSGi系统中选择多个不同版本Package的问题。本节我们继续沿用前面的例子深化理解多版本Package共存时的约束。Bundle A、B、C、D的定义如下:
Bundle A:
Import-Package:org.osgi.service.http
Import-Package:javax.servlet.http

Bundle B:
Export-Package: org.osgi.service.http;uses:="javax.servlet.http";
Import-Package:javax.servlet.http; version="2.4"

Bundle C:
Export-Package: javax.servlet.http; version="2.1"

Bundle D
Export-Package: javax.servlet.http; version="2.4"
对于Bundle A来说,由于没有指明所需要的javax.servlet.http的版本号,Bundle C和Bundle D对它来说都是满足需求的。但是它依赖的org.osgi.service.http来自于Bundle B之中,Bundle B明确声明了org.osgi.service.http包要用到版本为2.4的javax.servlet.http的类,在这层隐含限制下,Bundle A就不允许从Bundle C获取2.1版的javax.servlet.http了,如图2-8所示。

图2-8 多版本Package依赖选择示例(1)
如果Bundle A在导入javax.servlet.http的时候声明了只支持版本2.1(在导入时加入version="[2.1.0,2.1.0]"),其他条件不变,那么就会发生版本冲突,Bundle A在这种设置下是无法解析通过的,但是不会影响到Bundle B、Bundle C和Bundle D,它们依然能够解析成功,如图2-9所示。

图2-9 多版本Package依赖选择示例(2)
遇到上述Bundle解析问题,OSGi实现框架会抛出一个BundleException异常,可能的异常信息(根据实现框架而定,这里以Equinox框架为例)类似如下所示:
org.osgi.framework.BundleException:
The bundle "BundleA_1.0.0.qualifier [8]" could not be resolved. Reason: Package
uses conflict: Import-Package: org.osgi.service.http; version="0.0.0"
再把场景修改一下,假设在Bundle B导出org.osgi.service.http时没有使用uses参数,在实际代码之中org.osgi.service.http也不会依赖javax.servlet.http中的类,其他条件不变,那么Bundle A、B、C、D全部能正常共存。因为Bundle A认为在使用org.osgi.service.http时不会遇到任何与javax.servlet.http有依赖关系的类,这样即使Bundle B中有其他的包需要2.4版的javax.servlet.http支持,也对Bundle A没有任何影响,因为根本不会使用到uses参数,如图2-10所示(注意Bundle B中已经没有了表示uses参数的图示)。

图2-10 多版本Package依赖选择示例(3)
我们继续假设,如果仅在元数据信息中没有使用uses参数描述出org.osgi.service.http对javax.servlet.http的依赖关系,而在实际代码之中它们却有依赖关系,那会怎样呢?OSGi规范并未说明这种元数据与实际代码不符的场景应当如何处理,因此这时不同实现框架表现出来的行为是不可预测的。在Equinox的实现中,Bundle A在实际使用到Bundle B发布出来的org.osgi.service.http中的类时并不会报错,但是没有遵循Bundle B的定义采用2.4版的javax.servlet.http包,而是采用了在Bundle A中引用的2.1版。
3.相同Package循环导出
我们知道,即使不考虑OSGi环境,在普通Java系统中,一个Package中的各个类也可以分布于不同JAR包中,这种情况很常见。因此OSGi必须允许多个Bundle导出相同的Package,即出现拆分包的情况。但是如果不加约束,在某些场景中会出现一些逻辑矛盾,例如接下来要介绍的相同Package循环导出的例子。Bundle A、B、C、D定义如下:
Bundle A:
Require-Bundle: BundleB, BundleC
Export-Package: p

Bundle C:
Require-Bundle: BundleD
Export-Package: p

Bundle B
Export-Package: p

Bundle D:
Export-Package: p
相同Package循环导出的图形化描述如图2-11所示。

图2-11 相同Package循环导出(1)
这4个Bundle都共同导出了一个包p,如果这时某个Bundle向Bundle A请求加载p中的类,Bundle A会根据深度优先的规则去查找这个类(OSGi的类加载器规则决定了加载请求优先委派给导入Bundle的类加载器,后面我们再详细介绍这一点),即查找顺序是:B->D->C->A。在这个基础上,再让Bundle D依赖Bundle A(加入Require-Bundle: BundleA),即变为如图2-12所示的状态。

图2-12 相同Package循环导出(2)
在这种场景下,Bundle A、C、D构成了一个循环,如果仍然按照深度优先搜索,就会出现B->D->C->A->B->D->C->A->……的无限循环。为了避免出现这样的循环,OSGi规范明确要求实现框架必须对每个搜索过的Bundle记录状态,保证每个Bundle只被搜索一次。因此对于图2-11所示的情况,搜索顺序依然是B->D->C->A。
4.Require-Bundle带来的问题
OSGi提倡使用Import-Package和Export-Package来构成模块之间的依赖关系,不提倡使用Require-Bundle,从实践经验来说,依赖的粒度总是越小越好。Bundle级别的导入还可能会由于Package重复导入和导出而带来一系列逻辑缺陷和性能问题。
对于“拆分包”的场景——如果Bundle A把它导入的Bundle B中某个Package使用的Export-Package重新导出就会形成拆分包,假如这时如果通过Bundle A加载这个Package,类搜索的路径就会变得更长,类加载器中抛出并接住ClassNotFoundException异常的次数会变得更多,这样就会带来额外的性能开销。
另外,Require-Bundle会让导出Package的过程变得更为复杂,可能导致Bundle开发人员不可预料地声明改变。因为在Require-Bundle中的声明的visibility可能会令Bundle的调用者无所适从。例如,有如下定义:
Bundle A:
Require-Bundle: BundleB;visibility:=reexport, BundleC;visibility:=private

Bundle B:
Export-Package: p

Bundle C:
Export-Package: p
上面的定义会导致Bundle A只导出“一部分”的Package p。根据定义内容,Bundle A由于在导入Bundle B时声明了“visibility:=reexport”,暗示了它会重复导出包p,但这又与Bundle A直接声明“Export-Package p”有所不同。当Bundle A收到类查找的请求时,类查找顺序应为B->C->A,如果Bundle B中的Package p确实有这个类,那么通过visibility:=reexport还是Export-Package p实现导出并没有区别;但是如果Bundle B中的Package p没有请求的类,被请求的类在Bundle A中或在者Bundle C的Package p中,那么这时OSGi框架就会拒绝提供这些类,这是因为这些类在未被导出的私有Package中或在明确声明为visibility:=private的Bundle中。因此Bundle A只导出了“一部分”的Package p。这种导出的复杂性从Bundle A、B、C内部定义来还能够解释得过去,但是从在外部调用者的角度看,Bundle A只导出“一部分”的Package p就显得很别扭了。上面示例对应的图形表述如图2-13所示。

图2-13 Require-Bundle导致的Package导出问题
2.4.3 校验Bundle有效性
要确定某个JAR包是否是一个合法的OSGi Bundle,首先要根据其元数据信息中的Bundle-ManifestVersion(注意,不是ManifestVersion,ManifestVersion是JAR文件规范中定义的)来确定元数据信息的版本号。如果在元数据信息中没有指定Bundle-ManifestVersion,那么其默认属性是1,表示遵循OSGi R3规范;如果Bundle-ManifestVersion为2,则代表遵循OSGi R4/R5规范;OSGi R4规范中添加了一些特定的标记。在将Bundle安装到OSGi系统的时候,必须对其进行有效性校验。下面列举一些(不完全包括)会导致Bundle有效性校验失败的问题。
无法根据Bundle-RequireExecutionEnvironment的值找到一个可匹配执行环境。
在Bundle中没有设置Bundle-SymbolicName标记。
存在重复的标记。
对某个Package重复导入。
Bundle导入或导出了以java.*开头的Package。
在Export-Package标记中用mandatory参数指明了某些强制属性,却没有定义这些属性。
尝试安装某个和OSGi框架中某个已经安装好的Bundle具有同样名称和版本的Bundle。
将某个Bundle更新为和OSGi框架中一个已经安装好的Bundle具有同样名称和版本的Bundle。
元数据信息中存在语法错误(例如,不合法的版本格式或Bundle名称)。
同时使用Specification-version和version参数,但是为它们指定了不一样的值,例如:
Import-Package p;specification-version=1;version=2
这将导致一个错误。而下面的指定:
Import-Package p;specification-version=1, q;version=2
不会导致错误。
在元数据信息中列出了权限文件:OSGI-INF/permission.perm,但此文件不存在。
Bundle-ManifestVersion的值不等于1或2,在今后的OSGi规范中另有规定的情况除外。

相关文章
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.5 系统Bundle
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.5节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1188 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一2.2 Bundle
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.2节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1391 0
|
容器
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.6 Bundle上下文
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.6节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1170 0
|
容器
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.1 Bundle标识
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.1节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1131 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一第二部分 OSGi规范与原理
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第二部分,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
911 0
|
Java 容器
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.2 Bundle状态及转换
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.2节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1730 0
|
Java API
《深入理解OSGi:Equinox原理、应用与最佳实践》一2.8 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第2章,第2.8节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1121 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一1.3 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第1章,第1.3节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1183 0
|
Java 应用服务中间件
《深入理解OSGi:Equinox原理、应用与最佳实践》一导读
笔者自2007年接触OSGi以来,曾在数个大型系统中使用过OSGi作为软件的基础架构,这一方面使笔者深刻感受到了OSGi带来的诸多好处以及OSGi设计思想的魅力;另一方面也使笔者注意到OSGi的入门门槛相对较高,如果没有足够的指导材料,开发人员从零开始学习并探索出OSGi的最佳实践需要很高的成本。
1396 0
《深入理解OSGi:Equinox原理、应用与最佳实践》一3.7 本章小结
本节书摘来自华章出版社《深入理解OSGi:Equinox原理、应用与最佳实践》一 书中的第3章,第3.7节,作者:周志明 谢小明,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1265 0