分类: Java
2010-12-19 19:44:27
第一次接触OSGi 是2006年看见的一则网上新闻,该新闻中提到BMW 汽车的通信-娱乐(infotainment)系统采用了OSGi 架构,这套系统主要用来控制汽车上的音箱、灯光、导航和通讯等设备,整个系统由1000多个模块组成,启动时间却只需要3.5秒钟,这对于一个基于 Java 的框架来讲,具有两个重大意义:一、说明了Java 执行效率并不差;二、OSGi 框架的性能尤其优秀。因此笔者对OSGi 框架产生了极大的兴趣,后来终于在一个项目中负责研究和开发基于OSGi 框架的应用程序,从此对它便情有独钟。令人欣慰的是,OSGi 在2007年取得了诸多战果:BEA 公司、Eclipse 基金会和Interface21 公司相继加入OSGi 联盟;在EclipseCon 2007大会上引起了业界的广泛关注,其中以Spring-OSGi、Web Service 与OSGi 等技术最为引人注目,这也标志着OSGi 将在未来与企业应用紧密结合;OSGi R4 标准发布,相关内容被成功的写入JSR 291 规范中;Spring 2.5 框架的发布,宣称其所有jar 包都兼容OSGi 标准;虽然OSGi 没能成功进入JavaEE 6 草拟计划中,但是Sun 公司宣称会在下一代Java EE 标准中重点考虑OSGi。因此笔者个人认为,在不久的将来OSGi 势必会在企业应用中发挥出强大的作用,基于OSGi 框架的产品也将层出不穷。本文从OSGi 的历史背景、OSGi 的特点、OSGi 开源框架介绍、OSGi 开发环境部署、OSGi 版的Hello World 五个部分对OSGi 框架进行概要的介绍,希望读者能从中有所收获。
OSGi 的历史背景
什么是OSGi 呢?OSGi——Open Service Gateway Initiative 字面上的意思是一个公共的服务平台。1999年OSGi 联盟成立,它是一个非盈利的国际组织,旨在建立一个开放的服务规范,为通过网络向设备提供服务建立开放的标准,是开放业务网关的发起者。OSGi 联盟的初始目标是构建一个在广域网和局域网或设备上展开业务的基础平台。历史总是具有惊人的相似性,正如Java 诞生于一个嵌入式开发的项目中,却被应用于网络平台的开发,对OSGi 的最早设计也是针对嵌入式应用的,诸如机顶盒、服务网关、手机、汽车等都是其应用的主要环境。后来,由于OSGi 的诸多优秀特性(可动态改变系统行为,热插拔的插件体系结构,高可复用性,高效性等等),它被应用于许多PC 上的应用开发,因此逐步为开发者所知和钟爱。现在人们对OSGi 的理解已经远远不是它字面和初衷所能解释的了,笔者认为称其为一个轻巧的、松耦合的、面向服务的应用程序开发框架更为确切一些。
OSGi 真正被大家所知还是和Eclipse 有密切关系的。Eclipse 很多年都是Java 开发者的首选IDE,相信只要是一个Java 开发者,应该没有人不知道Eclipse 的。在Eclipse 3.0 以前的版本中,它本身有一套自身的插件体系,而该插件体系的设计非常精巧细致,受到许多开发者的推崇,但是Eclipse 基金在Eclipse 3.0 发布的时候,做出了一个大胆的行为,就是将Eclipse 逐步迁移到OSGi 框架中,并自己实现了一个OSGi 开源框架,取名为Equinox,该框架随着每次Eclipse 的发布也会相应的更新。Eclipse 之所以这么做,其一是因为Eclipse 的插件体系与OSGi 的设计思想不谋而合,其二也是因为OSGi 更为规范,其对插件体系的定义也更为完整一些。事实证明Eclipse 在采用OSGi 架构后,无论从性能、可扩展性这两个方面来讲还是从二次开发的角度来定义,都取得巨大的成功。下图展示了Eclipse 与OSGi 框架的关系。
OSGi 的特点
在介绍OSGi 框架的特点之前,先简单的介绍一下OSGi 框架的各个部分,如下图所示。
解释一下上图中每一层的含义,其中OS 层和JVM 层可以不用详细介绍了,重点需要关注的是应用程序Bundles 层。框架本身提供的类加载,生命周期管理,服务注册和规范服务也都是针对Bundles 的。每一个在OSGi 框架中运行的逻辑单元称为一个Bundle,Bundle 实际是一个符合特定形式的jar 文件。每一个Bundle 的功能可以是抽象的也可以是具体的。所谓抽象,就是它不是一个具体的应用,没有完成一些业务功能,而只暴露了一些接口或者功能给其他的Bundle 使用;所谓具体,就是该Bundle 可以独立的完成一个功能,例如连接数据库,获取数据等等。Bundle 有六种状态,分别是:installed(安装完成,本地资源成功加载),resolved(依赖关系满足,即该Bundle 要么是准备好运行了,要么是已经被停止了),starting(Bundle 正在被启动),stopping(Bundle 正在被停止),active(Bundle 被激活,正在运行中),uninstalled(Bundle 被卸载了)。OSGi 有它自身的类加载机制从而控制这些加载的Bundles 彼此之间的依赖关系,而生命周期管理也是OSGi 的一大亮点,由于可动态的对这些加载的Bundles 进行安装、卸载、启动、停止等操作,所以可以动态的改变应用程序的运行状态。当一系列的Bundles 存在于服务器中的时候,那么它们之间必然会存在通信协作的部分,比如说一个通过网页捕获用户输入的Bundle 执行的时候,它必须首先需要一个Web 服务器服务的支持,那么这个时候服务注册器就会从整个OSGi容器中寻找这个服务,如果能完成服务的匹配,那么相应的功能就会很自然的实现了。OSGi 规范还规定了一组预设的服务,包括日志、服务管理等等,这些服务在主流的开源框架中都有实现。OSGi 框架中还包括一个安全层,OSGi 的安全层扩展了Java 的安全机制,增并加了一些新的约束以填补了Java安全机制中的遗漏。
一. 概述
OSGI SERVICE PLATFORM是一个基于JAVA的,开放并且提供统一接口标准的体系框架,
在OSGI中所有模块的部署都必须以Bundle 的方式来进行部署。基于这个体系框架,服务提供商,程序开发人员,软件提供商,服务网管运营商,设备提供商能够 协调地联合起来开发,部署以及管理向用户提供的各种服务。
(a) OSGI的优势:
1. 插件可热插拔 可以实现软件的动态升级
2. 代码维护 不同Bundle间依赖仅存在于接口的层面上, 有利于代码的维护。
3. 稳定、高效的系统
基于OSGI的系统采用的是微核机制,微核机制保证了系统的稳定性,微核机制的系统只要微核是稳定运行的,那么系统就不会崩溃,也就是说基于OSGI的系统不会受到运行在其中的Bundle的影响,不会因为Bundle的崩溃而导致整个系统的崩溃。
4. 规范的、可积累的模块
大部分软件公司都形成不了规范的模块开发方式,因为没有统一的规范的基础架构体系的定义,往往每个项目、每个产品都会因为架构师的偏好、技术的发展而导致模块的开发方式完全不同,这就使得软件公司在人员技能要求、培养上很难形成统一,而OSGI为这个问题提供了解决方案,基于OSGI的系统采用规范的模块开发、部署方式构建系统。
(b)Bundle划分规则
采用OSGi技术实现应用系统时,最终展现在我们面前的将是一个个的Bundle组件,那么一个应用系统应被划分为几个bundle合适呢?这就涉及到了bundle划分的实现粒度问题。我比较倾向于按层次划分的方式,下面的实例开发都将采用该种结构划分。
根据层次进行划分:
<1>Services Bundle:在OSGi中,服务是通过Java的接口来定义的,可以把定义服务的Java接口组成一个Services Bundle,并由这个Bundle向其它Bundle提供接口。
<2>服务提供者Bundle:实现Services Bundle提供的接口,并向OSGi框架注册服务。
<3>服务使用者Bundle:引用Services Bundle提供的接口,并向OSGi框架请求相应的服务。
二. 使用SpringDM的开发实例
a) 环境搭建
i. 添加Spring环境
下载spring dynamic module,当前版本是1.0-rc2。将以下jar放到eclipse/plugins目录下。
spring-osgi-core-1.0-rc2.jar |
spring-osgi-io-1.0-rc2.jar |
spring-osgi-extender-1.0-rc2.jar |
ii. 添加Spring-OSGI环境(SpringDM)
下载spring,当前版本2.5.1,将以下jar放到eclipse/plugins目录下。
spring-beans-2.5.1.jar |
spring-aop-2.5.1.jar(依赖aopalliance.osgi-1.0-SNAPSHOT.jar) |
spring-core-2.5.1.jar |
spring-context-2.5.1.jar |
重启eclipse,并在运行选项中钩选新添加的这些bundle。
b) 创建插件项目
i. 步骤:
1. 创建一个插件工程
2. 选择an OSGI framework,并选择standard方式,我建议使用standard方式方式,因为这样我们可以不局限于equinox,可以把它部署到其他标准的OSGI容器中了。
3.我们在这里不生成Activator,因为采用了SpringDM方式,这个工作都交给Spring来完成。
c) 以上三个步骤在建立每个Bundle时基本是相同的,我们在来逐个分析这三个Bundle的建立过程。
i. 建立Service Bundle定义类
1. 定义接口
public interface MySercice { |
public String getHello(); |
} |
2. 打开MANIFEST.MF文件的runtime节点,把这个接口export出去,供其他Bundle Import使用。
ii. 建立 ServiceImpl Bundle 实现类
1. 打开MANIFEST.MF文件的Dependencies节点,把刚才定义的那个接口import进来。
2. 定义接口实现类
public class MyServiceImpl implements MySercice { |
@Override |
public String getHello() { |
return "My name is Benson!"; |
} |
} |
3. 定义Spring-OSGI配置文件
Spring_service.xml文件
|
|
xmlns:xsi="" |
xmlns:aop="" |
xmlns:tx="" |
xmlns:osgi="" |
xsi:schemaLocation=" |
/spring-beans.xsd |
|
/spring-osgi.xsd |
|
/spring-aop-2.0.xsd |
|
/spring-tx.xsd"> |
|
|
|
|
代码中蓝色code部分是我们需要注意的地方,其中“xmlns:osgi=”这句引用,是我们的Bundle支持向OSGI环境中的发布与调用。
部分的意义是:
<1>首先把实现类以Spring Bean的方式注入
<2>
因为OSGI中Bundle之间的依赖都是在接口层面上的,所以我们interface中引用的是我们要发布的接口,而ref中才是我们真正要发布出去的服务,通常为接口的实现类。
4. 配置文件的加载
打开MANIFEST.MF文件的地MANIFEST.MF节点
Manifest-Version: 1.0 |
Bundle-ManifestVersion: 2 |
Bundle-Name: Serviceimp Plug-in |
Bundle-SymbolicName: serviceimp |
Bundle-Version: 1.0.0 |
Import-Package: com.zhouwei.service |
Spring-Context: META-INF/spring_service.xml |
|
注意最后一句是我们需要添加的,是SpringDM的标识,这样Bundle启动后就知道自己将以SpringDM的方式启动了。
iii. 建立Service调用类
1. Import提供服务的接口,具体方法见ServiceImpl Bundle中的过程。
2. 建立服务调用测试类
下面代码中的start,stop类SpringDM管理,会自动调用。
public class MyTest { |
private MyService myService; |
public void setMyService(MyService myService) { |
this.myService = myService; |
} |
void start() { |
System.out.println("start......."); |
System.out.println(myService.getHello()); |
} |
void stop() { |
System.out.println("stop....."); |
} |
} |
3. 编写SpringDM配置文件
a) 调用服务
( 理論上id可以起任何名字)
|
|
interface="com.benson.service.MyService"> |
//================================================= |
|
init-method="start" destroy-method="stop"> |
|
|
|
b) 上面Java代码中的start和stop 方法由Spring Bean维护。
c) 发布与调用服务的区别
发布服务是先定义Spring Bean,发布服务时引用该Bean。调用服务时,先使用
4. 配置文件加载
具体方法见ServiceImpl Bundle中对应部分的过程
d) 运行插件项目
i. 打开run对话框
ii. 创建新配置以启动osgi framework
iii. 钩选必需的bundle,
1. Eclipse自带
org.apache.commons.logging |
javax.servlet |
org.eclipse.equinox.http.jetty |
org.eclipse.equinox.http.servlet |
org.eclipse.osgi |
org.eclipse.osgi.services |
org.mortbay.jetty |
2. Spring和SpringDM支持
spring-beans-2.5.1.jar |
spring-aop-2.5.1.jar |
spring-core-2.5.1.jar |
spring-context-2.5.1.jar |
aopalliance.osgi-1.0-SNAPSHOT.jar |
spring-osgi-core-1.0-rc2.jar |
spring-osgi-io-1.0-rc2.jar |
spring-osgi-extender-1.0-rc2.jar |
3. 当然还有上面创建的三个BUndle
选择完毕后,点击 Validate Bundles来验证bundles间的依赖关系是否满足,若不满足,会报缺少包的提示信息;若满足,则可点击run,来启动osgi framework。
iv. OSGi控制台
如果osgi控制台中显示osgi>,而且没有任何错误信息,并且打印出我们期望的语句,则表明osgi 框架配置已成功。
在osgi>后输入ss命令,回车,会显示所有已经安装的bundle的名称,id以及状态。
输入s命令,会显示osgi服务注册表中所有已经注册的服务
三. 在纯OSGI环境中开发实例
a) 环境搭建
基本与SpringDM方式需要的环境相同,只是不用添加SpringDM那三个依赖的插件了。
b) 创建过程
i. 创建三个Bundle,与前文中SpringDM方式基本相同。需要注意的是下图中步骤一定选择生成Activator激活器,它是用来管理这个Bundle的生命周期的,在SpringDM中我们没有生成它适应为Bundle的生命周期已经由SpringDM代为管理了。
ii. 注册OSGi服务
在我们的接口实现Bundle中的Activator类的start方法中,把实现的这个服务注册到OSGI环境中去,具体方法如下:
public void start(BundleContext context) throws Exception { |
// 注冊服務 |
registration = context.registerService(MyService.class.getName(), new MyServiceImpl(), null); |
} |
iii. 使用OSGI服务
在调用服务的bundle的Activator的start方法中,我们首先实例化一个服务跟踪类ServiceTracker,它可以跟踪OSGI Service的变化状况我么可以从这类中获取具体的服务内容。
public void start(BundleContext context) throws Exception { |
//獲取tracker |
tracker = new ServiceTracker(context, MyService.class.getName(), null); tracker.open(); |
// TestOsgi是具体的业务实现类,在它里面调用服务。 new TestOsgi().startTest(tracker); } |
//业务实现类
public class TestOsgi { public void startTest(ServiceTracker st) { MyService service = (MyService) st.getService(); if (service != null) { System.out.println("~~~~~~~~~~~~~~~~~~~~~~"); System.out.println(service.getHello()); } else { System.out.println("服務獲取失敗!"); } } } |
c) 运行
运行时配置与第一种方式也基本相同,只是不用启动与SpringDM相关的bundle了。下图为运行结果。
*====================================
由于圖片不能直接從word復制過來,所有需要完整版的朋友可以到,这里還包括文章中的所有的源碼。
*=====================================