OSGI 理论知识

下面列出了主要的控制台命令:

表 1. Equinox OSGi 主要的控制台命令表
类别 命令 含义
控制框架 launch 启动框架
shutdown 停止框架
close 关闭、退出框架
exit 立即退出,相当于 System.exit
init 卸载所有 bundle(前提是已经 shutdown)
setprop 设置属性,在运行时进行
控制 bundle Install 安装
uninstall 卸载
Start 启动
Stop 停止
Refresh 刷新
Update 更新
展示状态 Status 展示安装的 bundle 和注册的服务
Ss 展示所有 bundle 的简单状态
Services 展示注册服务的详细信息
Packages 展示导入、导出包的状态
Bundles 展示所有已经安装的 bundles 的状态
Headers 展示 bundles 的头信息,即 MANIFEST.MF 中的内容
Log 展示 LOG 入口信息
其它 Exec 在另外一个进程中执行一个命令(阻塞状态)
Fork 和 EXEC 不同的是不会引起阻塞
Gc 促使垃圾回收
Getprop 得到属性,或者某个属性
控制启动级别 Sl 得到某个 bundle 或者整个框架的 start level 信息
Setfwsl 设置框架的 start level
Setbsl 设置 bundle 的 start level
setibsl 设置初始化 bundle 的 start level

MANIFEST.MF

MANIFEST.MF 可能出现在任何包括主类信息的 Jar 包中,一般位于 META-INF 目录中,所以此文件并不是一个 OSGi 特有的东西,而仅仅是增加了一些属性,这样也正好保持了 OSGi 环境和普通 Java 环境的一致性,便于在老的系统中部署。表 2 列出此文件中的重要属性及其含义:

表 2. MANIFEST.MF 文件属性
属性名字 含义
Bundle-Activator Bundle 的启动器
Bundle-SymbolicName 名称,一般使用类似于 JAVA 包路径的名字命名
Bundle-Version 版本,注意不同版本的同名 bundle 可以同时上线部署
Export-Package 导出的 package 声明,其它的 bundle 可以直接引用
Import-Package 导入的 package
Eclipse-LazyStart 是否只有当被引用了才启动
Require-Bundle 全依赖的 bundle,不推荐
Bundle-ClassPath 本 bundle 的 class path,可以包含其它一些资源路径
Bundle-RequiredExecutionEnvironment 本 bundle 必须的执行环境,例如 jdk 版本声明

回页首

重要的理论知识

好的,刚才我们已经从头到尾开发了一个基于 Equinox 框架的 Hello world 应用程序。我们发现似乎并不是很困难,很多工作 Eclipse 已经帮我们做好了,例如 Activator 代码框架和 MANIFEST.MF 文件,我们也学会了如何控制 OSGi 的控制台和编写 MANIFEST.MF 文件,但是,您真的明白它们是如何运行的么?下面我们将重点介绍一些 OSGi 运行必备的基础知识。

什么是 bundle?

我们已经看到,编写一个很普通的 Hello world 应用,必须首先创建一个 plug-in 工程,然后编辑其 Activator 类的 start 方法,实际我们这样做的本质是为 OSGi 运行环境添加了一个 bundle,那么一个 bundle 必须的构成元素是哪些呢?

  1. MANIFEST.MF:描述了 bundle 的所有特征,包括名字、输出的类或者包,导入的类或者包,版本号等等,具体可以参考 表 2. MANIFEST.MF 文件属性。
  2. 代码:包括 Activator 类和其它一些接口以及实现,这个和普通的 Java 应用程序没有什么特殊的区别。
  3. 资源:当然,一个应用程序不可能没有资源文件,比如图片、properties 文件、XML 文件等等,这些资源可以随 bundle 一起存在,也可以以 fragment bundle 的方式加入。
  4. 启动级别的定义:可以在启动前使用命令行参数指定,也可以在运行中指定,具体的 start level 的解释,请参考 后面的说明。

框架做了些什么?

好了,我们已经明白 bundle 是什么了,也知道如何开发一个基本的 bundle 了,那么我们还必须要明白,我的 bundle 放在 Equinox 框架中,它对我们的 bundle 做了些什么?

图 11. Equinox 框架架构

实际上,目标平台已经为我们准备了 N 个 bundle,它们提供各种各样的服务,OSGi 中,这些 bundle 的名字叫 system bundle,就好比精装修的房子,您只需要拎包入住,不再需要自己铺地板,装吊顶了。

我们的 bundle 进入 Equinox 环境后,OSGi 框架对其做的事情如下:

  1. 读入 bundle 的 headers 信息,即 MANIFEST.MF 文件;
  2. 装载相关的类和资源;
  3. 解析依赖的包;
  4. 调用其 Activator 的 start 方法,启动它;
  5. 为其提供框架事件、服务事件等服务;
  6. 调用其 Activator 的 stop 方法,停止它;

Bundle 的状态变更

OK, 现在我们大概明白了一个 bundle 的定义和其在 OSGi 框架中的生命周期,前面我们看到控制台可以通过 ss 命令查看所有装载的 bundle 的状态,那么 bundle 到底具有哪些状态,这些状态之间是如何变换呢?我们知道了这些状态信息,对我们有何益处?

首先,了解一下一个 bundle 到底有哪些状态:

表 3. Bundle 状态表
状态名字 含义
INSTALLED 就是字面意思,表示这个 bundle 已经被成功的安装了
   
RESOLVED 很常见的一个状态,表示这个 bundle 已经成功的被解析(即所有依赖的类、资源都找到了),通常出现在启动前或者停止后
STARTING 字面意思,正在启动,但是还没有返回,所以您的 Activator 不要搞的太复杂
ACTIVE 活动的,这是我们最希望看到的状态,通常表示这个 bundle 已经启动成功,但是不意味着您的 bundle 提供的服务也是 OK 的
STOPPING 字面意思,正在停止,还没有返回
UNINSTALLED 卸载了,状态不能再发生变更了

下面请看一张经典的 OSGi bundle 变更状态的图:

图 12. OSGi bundle 变更状态图

Bundle 导入导出 package

OK,到现在为止,似乎一切都是新鲜的,但是您似乎在考虑,OSGi 到底有什么优势,下面介绍一下其中的一个特点,几乎所有的面向组件的框架都需要这一点来实现其目的:面向服务、封装实现。这一点在普通的 Java 应用是很难做到的,所有的类都暴露在 classpath 中,人们可以随意的查看您的实现,甚至变更您的实现。这一点,对于希望发布组件的公司来说是致命的。

图 13. OSGi bundle 原理

OSGi 很好的解决了这个问题,就像上面的图显示的,每个 bundle 都可以有自己公共的部分和隐藏的部分,每个 bundle 也只能看见自己的公共部分、隐藏部分和其它 bundle 的公共部分。

bundle 的 MANIFEST.MF 文件提供了 EXPORT/IMPORT package 的关键字,这样您可以仅仅 export 出您希望别人看到的包,而隐藏实现的包。并且您可以为它们编上版本号,这样可以同时发布不同版本的包。

Bundle class path

这一点比较难理解,一般情况下您不需要关心这个事情,除非事情出现了问题,您发现明明这个类就在这里,怎么就是报告 ClassNotFoundException/NoClassDefExcpetion 呢?在您垂头丧气、准备砸掉电脑显示器之前,请看一下 bundle 中的类是如何查找的:

  1. 首先,它会找 JRE,这个很明显,这个实际是通过系统环境的 JAVA_HOME 中找到的,路径一般是 JAVA_HOME/lib/rt.jar、tools.jar 和 ext 目录,endorsed 目录。
  2. 其次,它会找 system bundle 导出的包。
  3. 然后,它会找您的 import 的包,这个实际包含两种:一种是直接通过 require-bundle 的方式全部导入的,还有一种就是前面讲的通过 import package 方式导入的包。
  4. 查找它的 fragment bundle,如果有的话。
  5. 如果还没有找到,则会找自己的 classpath 路径(每个 bundle 都有自己的类路径)。
  6. 最后它会尝试根据 DynamicImport-Package 属性查找的引用。

启动级别 Start level

在 Equinox 环境中,我们在配置 hello world 应用的时候,看到我们将 framework start level 保持为 4,将 Hello world bundle 的 start level 设置为 5 。 start level 越大,表示启动的顺序越靠后。在实际的应用环境中,我们的 bundle 互相有一定的依赖关系,所以在启动的顺序上要有所区别,好比盖楼,要从打地基开始。

实际上,OSGi 框架最初的 start level 是 0,启动顺序如下:

  1. 将启动级别加一,如果发现有匹配的 bundle(即 bundle 的启动级别和目前的启动级别相等),则启动这个 bundle;
  2. 继续第一步,直到发现已经启动了所有的 bundle,且活动启动级别和最后的启动的 bundle 启动级别相同。

停止顺序,也是首先将系统的 start level 设置为 0:

  1. 由于系统当前活动启动级别大于请求的 start level,所以系统首先停止等于当前活动启动级别的 bundle;
  2. 将活动启动级别减一,继续第一步,直到发现活动启动级别和请求级别相等,都是 0。

回页首

开发一个真实的 OSGi 应用程序

我们不能只停留在 hello world 的层面,虽然那曾经对我们很重要 ,但是现实需要我们能够使用 OSGi 写出激动人心的应用程序,它能够被客户接受,被架构师认可,被程序员肯定。好的,那我们开始吧。下面将会着重介绍一些现实的应用程序可能需要的一些 OSGi 应用场景。

发布和使用服务

由于 OSGi 框架能够方便的隐藏实现类,所以对外提供接口是很自然的事情,OSGi 框架提供了服务的注册和查询功能。好的,那么我们实际操作一下,就在 Hello world 工程的基础上进行。

我们需要进行下列的步骤:

  1. 定义一个服务接口,并且 export 出去供其它 bundle 使用;
  2. 定义一个缺省的服务实现,并且隐藏它的实现;
  3. Bundle 启动后,需要将服务注册到 Equinox 框架;
  4. 从框架查询这个服务,并且测试可用性。

好的,为了达到上述要求,我们实际操作如下:

  1. 定义一个新的包 osgi.test.helloworld.service ,用来存放接口。单独一个 package 的好处是,您可以仅仅 export 这个 package 给其它 bundle 而隐藏所有的实现类
  2. 在上述的包中新建接口 IHello,提供一个简单的字符串服务,代码如下:
    清单 2. IHello
    package osgi.test.helloworld.service; 
    
    public interface IHello {
        /**
         * 得到 hello 信息的接口 .
         * @return the hello string.
         */
        String getHello();
    }
  3. 再新建一个新的包 osgi.test.helloworld.impl,用来存放实现类。
  4. 在上述包中新建 DefaultHelloServiceImpl 类,实现上述接口:
    清单 3. IHello 接口实现
    public class DefaultHelloServiceImpl implements IHello { 
    
        @Override
        public String getHello() {
            return "Hello osgi,service";
        } 
    
     }
  5. 注册服务,OSGi 框架提供了两种注册方式,都是通过 BundleContext 类实现的:
    1. registerService(String,Object,Dictionary) 注册服务对象 object 到接口名 String 下,可以携带一个属性字典Dictionary
    2. registerService(String[],Object,Dictionary) 注册服务对象 object 到接口名数组 String[] 下,可以携带一个属性字典Dictionary,即一个服务对象可以按照多个接口名字注册,因为类可以实现多个接口;

    我们使用第一种注册方式,修改 Activator 类的 start 方法,加入注册代码:

    清单 4. 加入注册代码
    public void start(BundleContext context) throws Exception { 
    
        System.out.println("hello world");
        context.registerService(
            IHello.class.getName(),
            new DefaultHelloServiceImpl(),
            null); 
    
    }
  6. 为了让我们的服务能够被其它 bundle 使用,必须在 MANIFEST.MF 中对其进行导出声明,双击 MANIFEST.MF,找到 runtime > exported packages > 点击 add,如图,选择 service 包即可:
    图 14. 选择导出的服务包

  7. 另外新建一个类似于 hello world 的 bundle 叫:osgi.test.helloworld2,用于测试 osgi.test.helloworld bundle 提供的服务的可用性;
  8. 添加 import package:在第二个 bundle 的 MANIFEST.MF 文件中,找到 dependencies > Imported packages > Add …,选择我们刚才 export 出去的 osgi.test.helloworld.service 包:
    图 15. 选择刚才 export 出去的 osgi.test.helloworld.service 包

  9. 查询服务:同样,OSGi 框架提供了两种查询服务的引用 ServiceReference 的方法:
    1. getServiceReference(String):根据接口的名字得到服务的引用;
    2. getServiceReferences(String,String):根据接口名和另外一个过滤器名字对应的过滤器得到服务的引用;
  10. 这里我们使用第一种查询的方法,在 osgi.test.helloworld2 bundle 的 Activator 的 start 方法加入查询和测试语句:
    清单 5. 加入查询和测试语句
    public void start(BundleContext context) throws Exception {
        System.out.println("hello world2"); 
    
        /**
            * Test hello service from bundle1.
        */
        IHello hello1 =
            (IHello) context.getService(
            context.getServiceReference(IHello.class.getName()));
            System.out.println(hello1.getHello());
    }
  11. 修改运行环境,因为我们增加了一个 bundle,所以说也需要在运行配置中加入对新的 bundle 的配置信息,如下图所示:
    图 16. 加入对新的 bundle 的配置信息

  12. 执行,得到下列结果:
    图 17. 执行结果

恭喜您,成功了!

时间: 2024-08-03 20:55:13

OSGI 理论知识的相关文章

TestNG学习-001-基础理论知识

此 文主要讲述用 TestNG 的基础理论知识,TestNG 的特定,编写测试过程三步骤,与 JUnit4+ 的差异,以此使亲对 TestNG 测试框架能够有一个简单的认知. 希望能对初学 TestNG 测试框架的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激! TestNG是什么?TestNG是一个灵感来自于 JUnit 和 NUnit 的一个设计用来简化广泛的测试需求的开源自动化测试框架,其引入了一些新的功能,使其功能更强大,而且易于使用,但是 TestNG 不是 JUnit 的扩展.它

堆和栈的理论知识

一.预备知识-程序的内存分配       1. 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)-由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.2.堆区(heap)-一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收.注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵.3.全局区(静态区)(static)-全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初

JDBC编程理论知识(1)

1.SUN公司为统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC 2.JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成. 组成JDBC的2个包: (1)java.sql.*; (2)javax.sql.*; 3.JDBC在程序中的位置: 4.JDBC的六个固定步骤 1,注册数据库驱动[利用反射] 2,取得数据库连接对象Connection 3,创建SQL对象 4,执行SQL命令,并返回结果集 5,处理结果集 6,

Android初级教程对大量数据的做分页处理理论知识

有时候要加载的数据上千条时,页面加载数据就会很慢(数据加载也属于耗时操作).因此就要考虑分页甚至分批显示.先介绍一些分页的理论知识.对于具体用在哪里,会在后续博客中更新. 分页信息 1,一共多少条数据 select count(*) from blacktb; 性能低下 原因: sql解析器先查询数据字典,把*转成所有的列名和列的类型 然后把每行数据提取出来 最后统计多少行数据 select count(常量) from blacktb; 高性能的查询 不需要每行的记录,只需要行数 2,指定每页

Winsock网络编程笔记(4)----基本的理论知识

前面的笔记记录了Winsock的入门编程,领略了Winsock编程的乐趣..但这并不能算是掌握了Winsock,加深理论知识的理解才会让后续学习更加得心应手..因此,这篇笔记将记录一些有关Winsock的基本理论知识,由于是一篇笔记,鉴于看书速度有限,主要内容会慢慢地填入..错误在所难免的,希望看客更正..(*^__^*) 嘻嘻…… Winsock网络编程笔记(4)----基本的理论知识,布布扣,bubuko.com

数据库入门理论知识介绍

数据库入门理论知识介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 前言: 1.目前90%以上的公司面临的运维的瓶颈都在后端 最常见的2大瓶颈就是: 1>.数据库(极难扩展): 2>.存储: 所以说做互联网的运维工程师要是把以上两点问题解决就可以轻松的搞定整个架构 2.什么是数据库: 查百度上有长篇大论了一下数据库特点,我们可以简单的理解: 数据库就是一个存放数据的仓库,这个仓库按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织,存储的,我们可以通过数

ipv4理论知识2-分类编址、ip分类、网络标识、主机标识、地址类、地址块

分类编址 ipv4的体系结构中有分类编址和无分类编址(后续会介绍到),在分类编址时,ipv4地址分为A.B.C.D.E这5类.每类占用的IP比例和个数如下图: ipv4分类识别 计算机以二进制方式存储ipv4地址,在这种情况下可以和方便的写出一种算法,通过一个连续检查过程来查找出该ip地址的类别.流程图如下: 网络标识和主机标识 对于A.B.C类IP地址,都可以划分为网络标识和主机标识两部分.其中网络标识和主机标识的长度随地址类别的不同变化.网络标识和主机标识其实很好理解就相当于我们使用的电信的

iOS Socket理论知识

iOS Socket理论知识 序言 网络七层由下往上分别为物理层.数据链路层.网路层.传输层.会话层.表示层和应用层.其中物理层.数据链路层和网络层通常被称作媒体称,是网络工程师所研究的对象;传输层.会话层.表示层和应用层则被称作主机层,是用户所面向和关心的内容. http协议对应与应用层 TCP/UDP协议对应于传输层 IP协议对应于网络层 三者本质上没有可比性.何况HTTP协议是基于TCP连接的.TCP/IP是传输层协议,主要解决数据如何在网络中传输:而HTTP是应用层协议,主要解决如何包装

计算机编程基础:栈、堆、堆栈概念区分及理论知识

一.预备知识—程序的内存分配  一个由c/C++编译的程序占用的内存分为以下几个部分  1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结  构中的栈.  2.堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 .注意它与数据结构  中的堆是两回事,分配方式倒是类似于链表,呵呵.  3.全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在  一块区域, 未