OSGi理论与实战

77
OSGi 理理理理理 BlueDavy http://china.osgiusers.org 09.09.15

description

和清华学子交流的一个OSGi PPT。

Transcript of OSGi理论与实战

Page 1: OSGi理论与实战

OSGi 理论和实战BlueDavy

http://china.osgiusers.org

09.09.15

Page 2: OSGi理论与实战

目标 学习 OSGi 规范 R4.1 ,掌握 OSGi 核心概念; 学习 OSGi R4 实现框架的实现机制,以更好

的使用这些框架; 学习基于 Equinox 开发 OSGi Based 的应用; 扩展 Equinox ,更好的基于 Equinox 来开

发各类应用; 学习分布式 OSGi ;

Page 3: OSGi理论与实战

大纲 OSGi 介绍 OSGi 实现框架 扩展 Equinox Equinox 高级实战 分布式 OSGi

Page 4: OSGi理论与实战

1 、 OSGi 介绍 在 OSGi 中,模块是什么? 模块之间的类是如何做到隔离的? 怎么访问其他模块中的类? 模块的生命周期是怎么样的? 模块之间怎么交互? OSGi 提供了哪些常用的 Service ?

Page 5: OSGi理论与实战

我期望的模块化、动态化 模块化( Java 7 中最重要的 Feature )

独立,不受其他模块的影响; 其他模块只能访问该模块对外提供的功能; 模块具备独立的生命周期,例如启动、停止、

更新等。 动态化

能动态的增加、更新或删除模块,而对于模块来说不需要做额外的处理;

你期望中的?

Page 6: OSGi理论与实战

OSGi 是什么 一个标准,基于此标准实现的框架可轻松

的构建模块化、动态化的系统; Dynamic Module System For Java ; JSR 232 、 JSR 291 ; 众多 Java Application Server ( Weblogic 、

Glassfish 等)所选择的底层框架;

Page 7: OSGi理论与实战

Bundle

Bundle 是 OSGi 中部署的最小单位,因此可以认为 Bundle 就是模块;

Bundle 就是一个普通的 jar 包,只是其在MANIFEST.MF 中加入了一些特殊的头信息,例如 Bundle-Name ;

Page 8: OSGi理论与实战

Bundle 类隔离机制 Java ClassLoader 机制

类由 ClassLoader 来实现加载, Java 动态化实现的基础;

默认情况下有 BootStrap ClassLoader(jre/lib 、jre/classes) 、 Extension ClassLoader(jre/lib/ext) 以及 System ClassLoader(-classpath 指定 ) 三个级别的ClassLoader ;

应用可自行实现 ClassLoader 以动态的加载类,或加载相应目录下的类;

Page 9: OSGi理论与实战

Bundle 类隔离机制 每个 Bundle 均为独立的 ClassLoader ,典

型的 Bundle 的 ClassLoader 结构如下所示:

System ClassLoader

Framework ClassLoader

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Page 10: OSGi理论与实战

Bundle 类共享机制 Java 经典的 Delegation 模型

在寻找类时首先从 parent classloader 中寻找,如找不到才在当前 classloader 中寻找;

Page 11: OSGi理论与实战

Bundle 类共享机制 Bundle 类加载过程

java.* 委派给 parent classloader 加载; bootdelegation 参数中配置的也委派给

parent classloader 加载,在 parent classloader 中未找到也继续下面的步骤;

是否在 Import-Package 中,在则委派给Export-Package 的 bundle 的 classloader ;

是否在 Require-Bundle 中,在则委派给相应的 Bundle ClassLoader ,未找到则继续下面步骤;

在 Bundle-Classpath 中寻找,未找到则继续下面的步骤;

在 fragments bundle 的 classpath 中寻找,未找到则继续下面的步骤;

是否在 Export-Package 中,未找到则继续下面的步骤;

是否在 DynamicImport-Package 中,在则委派给相应的 Bundle ClassLoader 寻找,不在则抛出 ClassNotFound 异常。

Page 12: OSGi理论与实战

Bundle 类共享机制 因此可以通过在 MANIFEST.MF 中定义

Require-Bundle 、 Import-Package 、 Export-Package 以及DynamicImport-Package 来实现类共享;

Import-Package: org.osgiusers.china.demo Export-Package: org.osgiusers.china.demo DynamicImport-Package: org.osgiusers.*

注意: OSGi 支持包的版本的定义! Import-Package:

org.osgiusers.china.demo;version=“[1.0,2.0)”

Page 13: OSGi理论与实战

Bundle 类共享机制 Import-Package 寻找机制

比较提供了相应 package 的 Bundle 的状态, resolved 优先于未 resolved 的;

如均为 resolved 的 bundle ,则比较 package 的版本,默认 package 版本为 0.0.0 ,版本高的优先于版本低的;

如版本也一样,则比较 bundle id , bundle id小的优先于大的,也就是先安装的 bundle 优先。

Page 14: OSGi理论与实战

Bundle 生命周期 OSGi 按照下面的状态转换方

式来管理 Bundle 的生命周期; 可通过 OSGi 提供的 API 来主

动改变 Bundle 的生命周期。 BundleContext.installBundle Bundle.start Bundle.update Bundle.stop Bundle.uninstall

Bundle 生命周期改变时所做的动作将直接影响动态化。

INSTALLED

RESOLVED

UNINSTALLED

ACTIVE

STOPPING

STARTING

start

stop

Page 15: OSGi理论与实战

Bundle 生命周期 Install Bundle 时要做的事情

解析 Bundle 的 MANIFEST.MF 信息,判断格式是否符合标准以及是否存在相同的 Bundle ;

分配新的 Bundle ID后; 生成 Bundle 对象,并放入已安装的 Bundles集合中,状态置为 INSTALLED 。

Page 16: OSGi理论与实战

Bundle 生命周期 Resolve Bundle 时要做的事情

寻找 Bundle 中所需依赖的包或 Bundle 是否存在以及被 resolve ,包括寻找匹配的 import-package 、 require-bundle 等,如寻找到则进入检查,检查完冲突后形成绑定关系,以便加载类时能直接加载;

冲突示例 A Bundle依赖

org.osgiusers.china.demo;version=1.0.0,org.osgiusers.china.user;version=2.0.0;

B Bundle 提供了 org.osgiusers.china.demo;version=1.0.0 ,但同时import 了 org.osgiusers.china.user;version=1.1.0

如成功,则将 Bundle 状态置为 RESOLVED 。

Page 17: OSGi理论与实战

Bundle 生命周期 Start Bundle 时要做的事情

检查 Bundle 的状态,如未 Resolve ,则先Resolve ;

寻找 MANIFEST.MF 中配置的 Bundle-Activator ,如寻找到,则调用其 start 方法;

将 Bundle 状态标识为 ACTIVE 。

Page 18: OSGi理论与实战

Bundle 生命周期 Stop Bundle 时要做的事情

卸载当前 Bundle 对外提供的所有 OSGi Service ,并释放当前 Bundle引用的所有 OSGi Service ;

调用 MANIFEST.MF 中对应的 Bundle-Activator的类的 stop 方法;

将 Bundle 状态置为 RESOLVED 。

Page 19: OSGi理论与实战

Bundle 生命周期 Uninstall Bundle 时要做的事情

检查 Bundle 状态,如状态为 ACTIVE ,则先STOP ;

释放 Bundle 对其他 Bundle 的类依赖; 如当前 Bundle 中有其他 Bundle 的类依赖,则进行记录,如没有则释放该 Bundle 的ClassLoader ;

将当前 Bundle 的状态置为 UNINSTALLED 。

Page 20: OSGi理论与实战

Bundle 生命周期 Update Bundle 时要做的事情

首先停止当前 Bundle ; 重新安装并 RESOLVE Bundle ; 恢复到 Bundle 更新之前的状态。

Page 21: OSGi理论与实战

Bundle 生命周期总结 除了 DynamicImport-Package 外,其他的 Package引

用哪个 Bundle 在 Resolve 时就已经决定了; 如希望更新 Bundle 所引用到的类,则必须进行

refresh 动作; 但 refresh 动作也只对目前处于 unresolve 状态的以及之前

uninstall 时还有其他 bundle 类依赖的 Bundle进行unresolve 动作,并重新 resolve 对他们有依赖的 bundle 。

从上可见,当 Bundle 有 Start 动作时,有可能会造成有 Bundle 的 ClassLoader 建立成功,当发生 Refresh动作时,有可能会造成某些 Bundle Resolve失败,也有可能造成某些 Bundle 重建 ClassLoader 。

Page 22: OSGi理论与实战

Bundle 交互 直接类访问的方式(不推荐),结合上面之前的Bundle 生命周期的介绍,为什么?;

Service 交互方式 直接通过

BundleContext.getServiceReference 以及BundleContext.registerService 这样的方式(使用不方便,不推荐);

Declarative Services 方式,类似 IoC type 2 ;

BundleBundle

JAVA

Operating System

Hardware

OSGi Framework

Serviceregistry

packagespackages

Page 23: OSGi理论与实战

Bundle 交互 Declarative Services (简称 DS )

Service-Oriented Component Model 需要对外提供的功能均定义为接口; 接口的实现定义以 XML 方式定义为 Component ; 按接口的方式访问其他 Bundle 的功能; 以 XML 方式定义对其他 Bundle 的功能的访问; Component 也是有生命周期的。

OSGi R4.0 的重大改进就是这点!

Page 24: OSGi理论与实战

Bundle 交互 对外提供服务示例

调用其他 Bundle 的服务示例

Page 25: OSGi理论与实战

Bundle 交互 提供服务关键配置

provide interface ,声明对外提供的接口; property ,可增加一些服务实现的关键词,以便匹配过滤,

例如 <property name=“KEY” value=“DB”/> 调用服务关键配置

cardinality 配置调用服务的数量,例如 0..1 、 1..n 、 0..n ;

policy 配置引用的服务变化时的策略,主要有 static 和 dynamic 这两种;

target 配置引用的服务的过滤,例如 target=“(KEY=DB)” ,这样即使有

多个提供了接口的服务,也只会注入配置了 KEY=DB 的这个;

Page 26: OSGi理论与实战

Bundle 交互 DS 1.1 的可喜变化

Service-Component后指定的文件可用通配符,例如: Service-Component: OSGi-INF/*.xml

可以配置 activate 和 deactivate 方法,并且提供了多种方法签名的支持;

bind 、 unbind 方法加了两种签名的支持; void <method-name>(ServiceReference) void <method-name>(<parameter-type>,Map)

增加的这个方法签名至关重要,尤其是对于按服务属性来选择服务的场景而言。

Page 27: OSGi理论与实战

Bundle 交互 Component 生命周期

如 Component 有对外提供服务,在没有其他 Component 需要此服务时,这个 Component 是不会被激活的,只有当有其他Component 需要此服务,且当前 Component 所需的服务均满足条件时,才会被激活;

如 Component没有对外提供服务,那么只要此 Component 所需依赖的服务均可用,则会被激活。

Page 28: OSGi理论与实战

Bundle 交互 Component 生命周期

当有 Bundle 启动时,会触发 Declarative services 的介入,以将其中的 Service-Component 对应的配置文件的Component 载入;

当有 Bundle 通过 BundleContext 直接注册服务时,也将触发 Declarative Services ,以检查是否有 Component 可被激活;

当有 Bundle STOP 时,也会触发 DS 的介入,以将相关的OSGi Service卸载和释放,并检查相关的 Component 是否要被销毁;

如何做到引用的服务更新了,但当前 Component并不会重新创建实例?

Page 29: OSGi理论与实战

R 4.2 将会带来的变化 Launch Framework API

这个 API 的引入就意味着以后可以以一种统一的方式在程序中启动 OSGi Framework 了,示例如下:

Map p=new HashMap();p.put(“org.osgi.framework.storage”,System.getProperties(“user.home”)+File.separator+”osgi”);FrameworkFactory factory=Class.forName(factoryClassName).newInstance();Framework framework=factory.newFramework(p);framework.init();BundleContext context=framework.getBundleContext();…// 安装 Bundlesframework.start();framework.waitForStop();

Page 30: OSGi理论与实战

OSGi 提供的服务 Http Service

以便支持简单的通过 http 访问 OSGi 应用; 但复杂的场景还是比较难支撑,这主要是由于目前还只有 jetty 实

现的 HttpService ,再加上它所支持的 Servlet 版本太低,这也导致了目前 OSGi 对于 Web 类型的应用而言支持不足,但可喜的是 RFC 66会带来很多变化;

Log Service 支持简单的日志记录,直接输出到 console ,不过可以结合 log4j

自行实现; EventAdmin Service

基于此可实现事件通知性质的功能; ConfigurationAdmin Service

基于此可实现配置信息的动态管理。

Page 31: OSGi理论与实战

OSGi 对模块化、动态化的满足程度 模块化

完全满足。 动态化

能够动态的增加、删除或更新模块,并完成依赖的 OSGi Service 的引用迁移;

但要自行处理由此带来的模块中对象的状态保持和恢复的问题,因此基于 OSGi 了,并不就代表应用一定可动态了,要合理的基于 Bundle 生命周期改变和 Component 生命周期改变会带来的变化做相应的处理。

Page 32: OSGi理论与实战

2 、 OSGi 实现框架 有哪些实现框架? 它们是如何实现 OSGi 规范的? 它们是否遵循 OSGi 标准? 对比而言,谁更有优势?

Page 33: OSGi理论与实战

OSGi 实现框架 Equinox

Equinox 是 R 4.0 的 RI ,并且也是 Eclipse 的核心,可谓是久经考验, Spring-DM 也是基于Equinox 。

Felix Felix 作为后起之秀,表现算得上是非常突出了。

其他 Oscar 、 Knoperfish已是昨日黄花了;

Page 34: OSGi理论与实战

OSGi 实现框架 Equinox

Equinox 的 ClassLoader 结构

System ClassLoader

Equinox ClassLoader

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Page 35: OSGi理论与实战

OSGi 实现框架 Equinox 中的类加载顺序

根据启动时的 bootdelegation属性判断是否需要从 parent classloader 中加载,具体parent classloader 是哪个,则可以通过 osgi.parentClassLoader属性来指定,支持的为: app ( SystemClassLoader )、 ext ( Extension ClassLoader )和 fwk ( Equinox ClassLoader )三种;

调用 Equinox 的 ClassLoaderDelegateHook 的 preFindClass 来加载; 从 Import-Package 中寻找; 从 Require-Bundle 中寻找; 尝试从当前 Bundle 中加载; 尝试从 DynamicImport-Package 中加载; 调用 Equinox 的 ClassLoaderDelegateHook 的 postFindClass 来加载; 尝试使用 Eclipse 的 buddy 机制来加载;

对比规范而言, Equinox 除了增加了 Hook 机制以及 buddy 机制外,完全遵守OSGi 规范。

Page 36: OSGi理论与实战

OSGi 实现框架 Equinox

Install Bundle 按照提供的 Bundle文件加载流,并根据

MANIFEST.MF 的信息形成 Bundle 对象,并将Bundle 的状态置为 INSTALLED ;

对外广播 Bundle INSTALLED 事件。

Page 37: OSGi理论与实战

OSGi 实现框架 Equinox

Resolve Bundle 如 bundle 为 singleton ,则首先检查 singletons

bundle 是否已存在,如存在则 resolve失败; 对所依赖的 package进行寻找、检查和绑定;

寻找 require-bundle 、 import-package匹配的 bundle ; 检查这些 bundle 之间是否有冲突,尽可能的选择没有冲突的 bundle ;

绑定通过检查的 bundle ,以便在加载类时可直接去这些bundle 中寻找。

将 Bundle 状态设置为 RESOLVED 。

Page 38: OSGi理论与实战

OSGi 实现框架 Equinox

Start Bundle 检查状态是否为 RESOLVED ,如未 resolve 则先进

行 resolve 动作, resolve失败则直接抛出异常,成功则继续下面步骤;

调用 MANIFEST.MF 中配置的 bundle-activator 对应的类的 start 方法。

将 Bundle 状态置为 ACTIVE 。

Page 39: OSGi理论与实战

OSGi 实现框架 Equinox

Stop Bundle 注销发布的 OSGi服务以及释放对其他 OSGi服务的引用;

调用 MANIFEST.MF 中配置的 bundle-activator 的类的 stop 方法;

将 Bundle 状态置为 RESOLVED 。

Page 40: OSGi理论与实战

OSGi 实现框架 Equinox

Uninstall Bundle 如 Bundle 为 Active ,先停止 Bundle ; 检查是否有其他 Bundle依赖了当前 Bundle 的

package ,如有则先不关闭此 bundle 的classloader ,如没有则关闭;

将 Bundle 状态置为 UNINSTALLED 。

Page 41: OSGi理论与实战

OSGi 实现框架 Equinox

Update Bundle 如状态为 ACTIVE ,先停止; 创建新的 Bundle 对象,如有 Bundle依赖老版本的

Bundle ,则暂时将老版本的 Bundle放入removalPendings 中,如没有,则清除老版本Bundle 的所有信息;

恢复新版本的 Bundle 到更新前的状态。

Page 42: OSGi理论与实战

OSGi 实现框架 Refresh

找到所有 removalPendings 中的bundle , unresolve 这些 bundle ,并重新resolve依赖了这些 bundle 的 bundle ;

恢复重新 resolve 了的 bundle 的状态。

Page 43: OSGi理论与实战

OSGi 实现框架 Equinox

DS 在这些过程中的处理 Start Bundle 时

扫描MANIFEST.MF 中对应的 Service-Component 指定的文件,加载这些文件; 判断其中指定的 Component 是否符合激活条件,如符合,则激活并调用 activate

方法,对于提供了 OSGi Service 的 Component ,则注册此 OSGi Service ; Stop Bundle 时

不做任何动作; 有服务注册时

寻找当前的 Component 是否有可激活的,如有则进行激活; 寻找当前已激活的 Component 中是否有需要此 OSGi Service 的,如有则调用其

bind-method进行注入; 有服务注销时

对于依赖了此 OSGi Service 的 component ,调用其 unbind-method ; 检查是否有 Component 因此不到激活条件的,则尝试调用 Component 的

deactivate 方法并销毁 Component 的实例。

Page 44: OSGi理论与实战

OSGi 实现框架 Felix

Felix 的 ClassLoader 结构

System ClassLoader

Felix ClassLoader

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Bundle ClassLoader

Bundle-Classpath中指定的 jar或路

Page 45: OSGi理论与实战

OSGi 实现框架 Felix 中的类加载顺序

判断是否为 bootdelegation 中配置的 package ,如交由Felix ClassLoader进行加载;

尝试从 Import-Package 中加载,如在则交由相应的Bundle ClassLoader去加载,即使加载不到也继续下面的步骤;

尝试从当前 Bundle 的 Classpath 中加载; 尝试从 DynamicImport-Package 中加载;

从上面步骤来看, Felix 在 Import-Package 时这步上未遵循 OSGi 规范,这有些时候可能会造成一些问题。

Page 46: OSGi理论与实战

OSGi 实现框架 Felix

Install Bundle 解析 Bundle 的 MANIFEST.MF文件,读取 OSGi 标准

中定义的头信息; 检查 Bundle 的唯一性,如不唯一则抛出异常; 将 Bundle 状态置为 INSTALLED 。

Page 47: OSGi理论与实战

OSGi 实现框架 Felix

Resolve Bundle 寻找需要依赖的 Package 或 Bundle ,并将其中未

resolve 的 Bundle进行 resolve ,如 resolve失败,则将其从潜在的 Bundle列表中删除,这步完全遵循 OSGi 规范;

检查是否有符合条件的 Bundle ,如没有,则抛出异常,有则继续;

检查这些符合条件的潜在的 Bundle ,如多个同名称的单态 Bundle 、版本冲突等问题,如这些问题均不存在,则继续下面的步骤;

完成绑定。

Page 48: OSGi理论与实战

OSGi 实现框架 Felix

Start Bundle 检查 Bundle 状态,如未 resolve 则先 resolve ; 执行 MANIFEST.MF 中对应的 Bundle-Activator 配置

的类的 start 方法,如未配置则跳过此步; 将 Bundle 状态设置为 ACTIVE ;

Page 49: OSGi理论与实战

OSGi 实现框架 Felix

Stop Bundle 执行 MANIFEST.MF 中对应的 Bundle-Activator 配置

的类的 stop 方法,如未配置则跳过此步; 注销 Bundle 对外发布的 Service ; 释放 Bundle引用的 Service ;

Page 50: OSGi理论与实战

OSGi 实现框架 Felix

Uninstall Bundle 检查状态,如为 ACTIVE ,则先停止; 如有其他 Bundle引用了当前 Bundle 的 package ,

则将当前 Bundle 的 RemovalPending设置为 true ; 将 Bundle 状态设置为 UNINSTALLED ; 如未被其他 Bundle 使用,则对当前 Bundle执行

refresh 动作;

Page 51: OSGi理论与实战

OSGi 实现框架 Felix

Update Bundle 停止当前的 Bundle ; 重新读取并解析 Bundle流,将 Bundle 状态置为

INSTALLED ; 恢复 Bundle 到更新前的状态。

Page 52: OSGi理论与实战

OSGi 实现框架 Felix

Refresh 在指定 Bundle 的情况下,递归找到依赖当前 Bundle 提

供的 package 的 Bundle ,作为此次 refresh 动作需要操作的 Bundles ;在未指定 Bundle 的情况下,则将已卸载的且 RemovalPending 为 true 的 Bundles ,以及还未resolve 的 Bundle 作为此次需要操作的 Bundles ;

refresh负责将需要 uninstall 的 Bundle 对外 export 的package彻底关闭,并对需要依赖这些 package 的 bundle执行 STOP 动作;

对于不需要 Uninstall但在 refresh 动作范畴的 bundles恢复为之前的状态。

Page 53: OSGi理论与实战

OSGi 实现框架 Felix

DS 在这些过程中的处理 和 Equinox 基本一致; 但值得注意的是 Felix DS 1.0.8 对于

policy=dynamic 、 cardinality=“0..1” | “0..n” 这类依赖服务变更时的处理有个 bug ,会造成在依赖的服务更新后出现需要此服务的 Component拿不到服务引用的现象。

Page 54: OSGi理论与实战

OSGi 实现框架对比对比点 Equinox Felix

模块化 遵循 OSGi R4 规范的基础上,做了一些之前

eclipse 的扩展机制,例如 buddy 等;

在 ClassLoader 的 控 制 上 提 供 了

osgi.parentClassLoader 以 及

ClassLoaderDelegateHook 方式允许使用者更

加方便的控制类的加载。

基本遵循 OSGi R4 规范,没有提供更多的扩展

方式。

动态化 在 Resolve Bundle 时即已决定 Bundle 所需依

赖的 Package 的 Bundle ,因此对于动态化所

需的对象引用迁移上,如 Bundle 之间是直接

访问类的方式,那么只有通过 refresh 动作才

有可能(例如对于已经在运行的 Bundle ,假

设它依赖了一个低版本的 package ,此时即使

安装了新版本 package 的 bundle 也无效)完

成对象引用的迁移,但要达到对象引用迁移的

变更,必须接受 classloader 重建的动作,而

对于基于 OSGi Service 进行交互的 Bundle 而

言,则稍微好一些,动态更新时有可能最多造

成 OSGi Component 实例的重建,甚至可以通

过 policy=dynamic 、 cardinality=“0..1” |

“0..n” 这样的方式使得无需重建实例,保持对

象状态。

和 Equinox 相同。

只是在 DS 方式下没办法做到不重建实例完成

更 新 , 因 为 在 Felix DS 1.0.8 下无法配 置 为

policy=dynamic 、 cardinality=“0..1” | “0..n” 的

方式。

Page 55: OSGi理论与实战

3 、扩展 Equinox

如何在外部程序中启动 Equinox ? 是否可以对 Equinox进行扩展? 如可以扩展,应如何做?

Page 56: OSGi理论与实战

扩展 Equinox 在外部程序中启动 Equinox

示例 // 配置 Equinox 的启动

FrameworkProperties.setProperty("osgi.noShutdown", "true");FrameworkProperties.setProperty("eclipse.ignoreApp", "true");FrameworkProperties.setProperty("osgi.bundles.defaultStartLevel", "4");FrameworkProperties.setProperty("osgi.bundles", "// 指定需要装载的 Bundle 的路径 ");// 指定需要加载的 bundles 所在的目录FrameworkProperties.setProperty("osgi.syspath", "// 例如 plugins");EclipseStarter.run(new String[]{"-configuration","configuration","-console"}, null);// 通过 EclipeStarter获取到 BundleContextcontext=EclipseStarter.getSystemBundleContext();

到了 R 4.2后就可以遵循标准的 API 来启动了

Page 57: OSGi理论与实战

扩展 Equinox

Equinox 中可用的扩展 ClassLoadingHook ClassLoaderDelegateHook ClassLoadingStatsHook AdaptorHook

借助这些扩展可以更好的控制 Equinox 。

Page 58: OSGi理论与实战

扩展 Equinox

ClassLoadingHook 可用于拦截 Bundle ClassLoader 的创建、类的

处理、 Classpath 的处理等; ClassLoaderDelegateHook

可用于在某个阶段干预类的加载,例如在类从 Import-Package 中扫描之前或在 DynamicImport-Package扫描之后。

Page 59: OSGi理论与实战

扩展 Equinox

ClassLoadingStatsHook 可用于拦截类从 Bundle-Classpath 中加载的动作,包括

加载之前以及加载之后。 AdaptorHook

可用于拦截 BaseAdaptor 的创建过程,例如在初始化时、 framework 启动时、停止时。

Page 60: OSGi理论与实战

扩展 Equinox

注册 Hook 类的方法 属性中指定

在启动时指定 osgi.hook.configurators ,但要注意的是要加上目前 Equinox 所需的 hooks 配置,格式为:hook.configurators=hook 类名 ,hook 类名

此文件需要放在 Equinox 框架的 classpath 下; 配置文件中指定

增加一个 hookconfigurators.properties ,其中的格式为:

hook.configurators=hook 类名 ,hook 类名

Page 61: OSGi理论与实战

Equinox 扩展的启示 框架级的软件都应提供良好的扩展支持,方式

可以为: Filter

可通过扩展 OSGi Service 模型来实现; 适合组装为一连串的处理来完成一个功能。

Interceptor 可通过扩展 OSGi Service 模型来实现; 适合方法执行前后的扩展处理;

Hook 直接基于 OSGi Service 模型即可实现; 适用于方法执行过程中的扩展处理;

Page 62: OSGi理论与实战

4 、 Equinox 高级实战 将上节的东西应用到实战中! 如何控制 Equinox 的 ClassLoader ? 如何将非 DS 的应用迁移为 DS 方式? 如何应对采用 OSGi后一些常见的错误?

Page 63: OSGi理论与实战

Equinox 高级实战 控制 Equinox 的 ClassLoader

基于 ClassLoaderDelegateHook 实现 实现 ClassLoaderDelegateHook 以及 HookConfigurator

接口; 可自行控制是在 Import-Package 寻找前进行类或资源

加载的处理还是在之后; 增加 hook.configurators文件或修改启动属性:

osgi.hook.configurators 。 实际的一个场景

有些 Bundle 类需要加载外部容器中的类,就可以用这种方法。

Page 64: OSGi理论与实战

Equinox 高级实战 将非 DS 的应用迁移为 DS 方式

迁移的方法 将之前直接类的调用方式改为基于接口的方式,这也

将对动态化的实现更为有利; 将接口放入一个独立的 Bundle 中,也是为了动态化; 将注册服务的代码去掉,改为通过编写 xml文件来注册服务;

将调用服务时依赖 BundleContext获取的方式去掉,改为 set 注入的方式,并通过 unset 来感知服务的注销;

编写 XML文件来定义需要调用的 OSGi Service ; 最后就是在运行的 Bundle 中增加 DS 实现的 Bundle 。

Page 65: OSGi理论与实战

Equinox 高级实战 将非 DS 的应用迁移为 DS 方式

示例

迁移好处 解放之前对于 BundleContext.registerService 等

这些难用的用法; 避免需要自己基于 ServiceTracker 或

ServiceListener 来实现服务状态的感知。

Page 66: OSGi理论与实战

Equinox 高级实战 将非DS 的应用迁移为 DS 方式

DS 的四种经典用法 set 注入单个依赖的服务;

<reference name=… interface=… bind=… unbind=… policy=…/> set 注入单个指定标识的依赖的服务;

<reference name=… interface=… bind=… unbind=… policy=… target=“(key=DB)”/> set 注入多个依赖的服务,在调用时进行全调用,类似 filter chain 或

hook ; <reference name=… interface=… bind=… unbind=… policy=… cardinality=“0..n”/>

set 注入多个依赖的服务,调用时根据某标识来调用其中的一个服务,实现类似的 Factory 机制;

<reference name=… interface=… bind=… unbind=… policy=… cardinality=“0..n”/> set 方法签名则改为 void <method-name>(interface,Map) (必须是 DS 1.1+ )

从上面四种用法来看, DS甚至强于了现在所有的 IoC容器。

Page 67: OSGi理论与实战

Equinox 高级实战 经常会碰到的一些问题

ClassNotFoundException 几乎是所有使用 OSGi 的人都会经常碰到的问题; 解决的方法是掌握 Bundle Class 的加载方式;

ClassCastException 几乎是所有使用 OSGi 的人都会经常碰到的问题; 解决的方法是掌握 ClassLoader 的基本原理以及 Bundle Class 的加

载方式; OSGi Component没启动

这是比较麻烦的问题; 解决的方法首先是掌握 OSGi Component 的激活条件; 然后是通过 OSGi Console ,查看 log ,或仔细检查

Component .xml 的配置;

Page 68: OSGi理论与实战

5 、分布式 OSGi

有哪些实现框架? 它们是如何实现的? 对比而言,谁更有优势?

Page 69: OSGi理论与实战

分布式 OSGi

你所期望的?

我所期望的 透明的将 OSGi服务发布为可远程调用的服务; 透明的调用远程的 OSGi服务; OSGi服务的动态化感知不是必须的

由于这受限于网络等各种因素,而且如果服务有多级调用的话就会出现类似的雪崩效应;

Page 70: OSGi理论与实战

分布式 OSGi

Java远程通信 ( TCP/IP 、 UDP/IP 、多播) +NIO/BIO

Java远程 RPC RMI :基于代理、反射机制 Webservice :基于 http RPC 机制

Page 71: OSGi理论与实战

分布式 OSGi

分布式 OSGi R-OSGi

老牌,有一定的优势,但用户群不多; CXF DOSGi

作为将来的 R 4.2 的 RFC119 的实现,值得期待!

Page 72: OSGi理论与实战

分布式 OSGi

R-OSGi 能达到的效果

透明的将 OSGi Service 对外发布; 只需要在注册 OSGi Service 时加上

properties.put(RemoteOSGiService.R_OSGi_REGISTRATION, Boolean.TRUE);属性

透明的调用远程的 OSGi Service ; 只需获取 RemoteOSGiService ,然后就可以调用远程的 OSGi

Service ; 由于调用时基于的是 TCP/IP ,例如其提供了基于 mina 的调用,

因此性能还是不错的; 可基于 jSLP 实现 Service Discovery ,从而更加简单的找到需要调

用的远程 OSGi Service 。

Page 73: OSGi理论与实战

分布式 OSGi

R-OSGi 实现机制

监听了服务注册事件,当有服务注册时,检查其注册的属性,如属性中有RemoteOSGiService.R_OSGi_REGISTRATION ,并且值为 true ,那么则做一个服务接口名与此服务的映射,并将此映射作为 tcp/ip某端口的处理器集合。

对于客户端,当通过 RemoteOSGiService拿到引用时,为一个服务端调用的代理,当调用此引用的方法时,即代理访问到远端服务器,过程即为一个类似的自定义的 java远程 RPC 。

Page 74: OSGi理论与实战

分布式 OSGi

CXF DOSGi 能达到的效果

透明的将 OSGi Service 对外发布; 只需在注册服务时增加如下属性即可:

props.put("osgi.remote.interfaces", "*"); props.put("osgi.remote.configuration.type", "pojo"); props.put("osgi.remote.configuration.pojo.address", WebService

URL); 透明的调用远程 OSGi Service

增加一个 remote-services.xml ,按照格式描述所需调用的远程 OSGi Service ;

调用时直接以接口的方式调用远程 OSGi Service即可,完全透明; 从这点对比而言, CXF DOSGi 要强于 R-OSGi ,毕竟 R-OSGi 的完全透明还是需要做点包装的。

Page 75: OSGi理论与实战

分布式 OSGi

CXF DOSGi 实现机制

监听服务注册事件,当注册时有相应的属性时,则将其发布为 Webservice ;

对于客户端,则通过扫描相应的 service-description的描述来生成一个调用远程 Webservice 的代理,并将此代理注册为相应的 OSGi Service 。

Page 76: OSGi理论与实战

分布式 OSGi

个人的经验 分布式 OSGi 的实现多数和 OSGi并没有多大的关系,其核心原理均为实现一个透明的远程RPC ;

如需关注性能、稳定性来说完全可以自行实现; 所需的知识为代理、反射、网络通信以及服务

发现;

Page 77: OSGi理论与实战

END!