下面列出了主要的控制台命令:
类别
命令
含义
控制框架
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 可能出现在任何包括主类信息的 Jar 包中,一般位于 META-INF 目录中,所以此文件并不是一个 OSGi 特有的东西,而仅仅是增加了一些属性,这样也正好保持了 OSGi 环境和普通 Java 环境的一致性,便于在老的系统中部署。表 2 列出此文件中的重要属性及其含义:
属性名字
含义
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 运行必备的基础知识。
我们已经看到,编写一个很普通的 Hello world 应用,必须首先创建一个 plug-in 工程,然后编辑其 Activator 类的 start
方法,实际我们这样做的本质是为 OSGi 运行环境添加了一个 bundle,那么一个 bundle 必须的构成元素是哪些呢?
好了,我们已经明白 bundle 是什么了,也知道如何开发一个基本的 bundle 了,那么我们还必须要明白,我的 bundle 放在 Equinox 框架中,它对我们的 bundle 做了些什么?
实际上,目标平台已经为我们准备了 N 个 bundle,它们提供各种各样的服务,OSGi 中,这些 bundle 的名字叫 system bundle,就好比精装修的房子,您只需要拎包入住,不再需要自己铺地板,装吊顶了。
我们的 bundle 进入 Equinox 环境后,OSGi 框架对其做的事情如下:
start
方法,启动它;stop
方法,停止它;OK, 现在我们大概明白了一个 bundle 的定义和其在 OSGi 框架中的生命周期,前面我们看到控制台可以通过 ss
命令查看所有装载的 bundle 的状态,那么 bundle 到底具有哪些状态,这些状态之间是如何变换呢?我们知道了这些状态信息,对我们有何益处?
首先,了解一下一个 bundle 到底有哪些状态:
状态名字
含义
INSTALLED
就是字面意思,表示这个 bundle 已经被成功的安装了
RESOLVED
很常见的一个状态,表示这个 bundle 已经成功的被解析(即所有依赖的类、资源都找到了),通常出现在启动前或者停止后
STARTING
字面意思,正在启动,但是还没有返回,所以您的 Activator 不要搞的太复杂
ACTIVE
活动的,这是我们最希望看到的状态,通常表示这个 bundle 已经启动成功,但是不意味着您的 bundle 提供的服务也是 OK 的
STOPPING
字面意思,正在停止,还没有返回
UNINSTALLED
卸载了,状态不能再发生变更了
下面请看一张经典的 OSGi bundle 变更状态的图:
OK,到现在为止,似乎一切都是新鲜的,但是您似乎在考虑,OSGi 到底有什么优势,下面介绍一下其中的一个特点,几乎所有的面向组件的框架都需要这一点来实现其目的:面向服务、封装实现。这一点在普通的 Java 应用是很难做到的,所有的类都暴露在 classpath 中,人们可以随意的查看您的实现,甚至变更您的实现。这一点,对于希望发布组件的公司来说是致命的。
OSGi 很好的解决了这个问题,就像上面的图显示的,每个 bundle 都可以有自己公共的部分和隐藏的部分,每个 bundle 也只能看见自己的公共部分、隐藏部分和其它 bundle 的公共部分。
bundle 的 MANIFEST.MF 文件提供了 EXPORT/IMPORT package 的关键字,这样您可以仅仅 export 出您希望别人看到的包,而隐藏实现的包。并且您可以为它们编上版本号,这样可以同时发布不同版本的包。
这一点比较难理解,一般情况下您不需要关心这个事情,除非事情出现了问题,您发现明明这个类就在这里,怎么就是报告 ClassNotFoundException/NoClassDefExcpetion 呢?在您垂头丧气、准备砸掉电脑显示器之前,请看一下 bundle 中的类是如何查找的:
JAVA_HOME
中找到的,路径一般是 JAVA_HOME/lib/rt.jar、tools.jar 和 ext 目录,endorsed 目录。在 Equinox 环境中,我们在配置 hello world 应用的时候,看到我们将 framework start level 保持为 4,将 Hello world bundle 的 start level 设置为 5 。 start level 越大,表示启动的顺序越靠后。在实际的应用环境中,我们的 bundle 互相有一定的依赖关系,所以在启动的顺序上要有所区别,好比盖楼,要从打地基开始。
实际上,OSGi 框架最初的 start level 是 0,启动顺序如下:
停止顺序,也是首先将系统的 start level 设置为 0:
我们不能只停留在 hello world 的层面,虽然那曾经对我们很重要 ,但是现实需要我们能够使用 OSGi 写出激动人心的应用程序,它能够被客户接受,被架构师认可,被程序员肯定。好的,那我们开始吧。下面将会着重介绍一些现实的应用程序可能需要的一些 OSGi 应用场景。
由于 OSGi 框架能够方便的隐藏实现类,所以对外提供接口是很自然的事情,OSGi 框架提供了服务的注册和查询功能。好的,那么我们实际操作一下,就在 Hello world 工程的基础上进行。
我们需要进行下列的步骤:
好的,为了达到上述要求,我们实际操作如下:
定义一个新的包 osgi.test.helloworld.service
,用来存放接口。单独一个 package 的好处是,您可以仅仅 export 这个 package 给其它 bundle 而隐藏所有的实现类
在上述的包中新建接口 IHello
,提供一个简单的字符串服务,代码如下:
package osgi.test.helloworld.service;
public interface IHello {
/**
* 得到 hello 信息的接口 .
* @return the hello string.
*/
String getHello();
}
再新建一个新的包 osgi.test.helloworld.impl
,用来存放实现类。
在上述包中新建 DefaultHelloServiceImpl
类,实现上述接口:
public class DefaultHelloServiceImpl implements IHello {
@Override
public String getHello() {
return "Hello osgi,service";
}
}
注册服务,OSGi 框架提供了两种注册方式,都是通过 BundleContext
类实现的:
registerService(String,Object,Dictionary)
注册服务对象 object
到接口名 String
下,可以携带一个属性字典Dictionary
;registerService(String[],Object,Dictionary)
注册服务对象 object
到接口名数组 String[]
下,可以携带一个属性字典Dictionary
,即一个服务对象可以按照多个接口名字注册,因为类可以实现多个接口;我们使用第一种注册方式,修改 Activator
类的 start
方法,加入注册代码:
public void start(BundleContext context) throws Exception {
System.out.println("hello world");
context.registerService(
IHello.class.getName(),
new DefaultHelloServiceImpl(),
null);
}
为了让我们的服务能够被其它 bundle 使用,必须在 MANIFEST.MF 中对其进行导出声明,双击 MANIFEST.MF,找到 runtime > exported packages > 点击 add,如图,选择 service 包即可:
另外新建一个类似于 hello world 的 bundle 叫:osgi.test.helloworld2
,用于测试 osgi.test.helloworld
bundle 提供的服务的可用性;
添加 import package:在第二个 bundle 的 MANIFEST.MF 文件中,找到 dependencies > Imported packages > Add …,选择我们刚才 export 出去的 osgi.test.helloworld.service 包:
查询服务:同样,OSGi 框架提供了两种查询服务的引用 ServiceReference
的方法:
这里我们使用第一种查询的方法,在 osgi.test.helloworld2
bundle 的 Activator
的 start
方法加入查询和测试语句:
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());
}
修改运行环境,因为我们增加了一个 bundle,所以说也需要在运行配置中加入对新的 bundle 的配置信息,如下图所示:
执行,得到下列结果:
恭喜您,成功了!
手机扫一扫
移动阅读更方便
你可能感兴趣的文章