OSGi开发起步(Getting Started with OSGi)-1第一个OSGi模块
原文出自: http://neilbartlett.name/blog/osgi-articles/ - Getting Started with OSGi
http://hi.baidu.com/tinawhisper/home
本文系翻译文章,但并未严格按照原文翻译。
1. 第一个OSGi模块
本文期望展示OSGi开发环境的简洁性,因此这里没有使用eclipse开发环境。本文只使用文本编辑器和基本的命令行工具进行OSGi程序开发。
本文的第一个例子相对其他示例有些长,这是因为我们需要建立一个基本的工作环境。在开始之前,我们需要一个可以运行的OSGi框架。现在我们可以在三种开源框架中进行选择:Apache Felix,Knopflerfish和Equinox。本文所有编写的代码对以上三个框架来说都是可用的,只是在使得这些代码运行的指令上有所区别。本文选择eclipse使用的Equinox。首先需要找到eclipse安装目录下的org.eclipse.osgi_3.2.1.R32x_v20060919.jar并复制此文件到一个空目录(文件中的版本号会根据安装eclipse的不同而不同)。为了使命令行参数短一些,把刚才复制过来的jar文件更名为equinox.jar。现在就可以在命令行中定位到刚才那个空目录并执行这个命令:
> java -jar equinox.jar -console
几秒钟后,命令窗口中会出现osgi>提示。如果是这样,就说明OSGi已经运行起来了(意外总会发生,比如你没有安装java!)。
osgi>命令行提示允许我们运行Equinox中的命令来进行整个框架的控制。可以在提示符后输入help获得所有命令的列表。经常使用的一个命令是ss。ss表示简要状态(short status)的意思。ss会列出当前已经安装到框架中所有模块(bundle)和它们状态的列表。在OSGi中bundle表示一个模块,这与eclipse中的插件(plug-ins)具有等同的概念。
输入ss后,Equinox输出为:
Framework is launched.
id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919
以上输出告诉我们当前只安装了一个已经启动的模块:系统模块(System bundle)。此模块是OSGi的一个总是安装并处于活动状态的特殊模块,它代表了框架本身。
现在我们开始编写一个模块。在相同的目录下,创建一个名称为HelloActivator.java的文件并复制以下代码到文件中:
import org.osgi.framework.*;
public class HelloActivator implements BundleActivator {
public void start(BundleContext context) { System.out.println("Hello Eclipse!"); }
public void stop(BundleContext context) { System.out.println("Goodbye Eclipse!"); }
}
一个模块还需要一个元文件(manifest file)对它的各种元信息进行声明:如模块的名称,版本等信息。所以现在还需要创建一个名称为HelloWorld.mf的文件并复制下文的文本到此文件中。特别需要注意的是要在此文本文件的最后保留一个空白行,否则打包命令jar在打包时会截短文件(最后一行丢失)。这样就会使得生产的jar包中的元文件缺失一行,导致jar文件读取和运行失败。
Manifest-Version: 1.0
Bundle-Name: HelloWorld
Bundle-Activator: HelloActivator
Bundle-SymbolicName: HelloWorld
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
现在另外打开一个命令行窗口使用如下命令构建一个jar文件:
> javac -classpath equinox.jar HelloActivator.java
> jar -cfm HelloWorld.jar HelloWorld.mf HelloActivator.class
回到原先的OSGi控制台,输入install file:HelloWorld.jar。控制台会输出"Bundle id is 1" ,接着输入ss命令就可以得到如下输出信息:
Framework is launched.
id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919
1 INSTALLED HelloWorld_1.0.0
刚才开发的HelloWorld模块安装到了OSGi框架中,但此模块还没有运行起来。现在输入命令start 1。命令中的1是安装HelloWorld模块完成后,OSGi赋值给此模块的ID号。当运行启动命令后,控制台显示消息"Hello Eclipse!"。接着输入stop 1就会得到消息"Goodbye Eclipse!"。
这些命令都做了些什么事情?上面的代码实现了一个BundleActivator接口,这个接口用于允许框架通知我们一些重要的生命期事件。当模块启动时,框架调用start方法,而如果模块停止时,框架则调用stop方法。还有一个要说明的是元文件中有一行声明"Bundle-Activator: HelloActivator"就是用于通知框架在一个模块中那个类是启动类(activator)。通常我们使用一个完整的类名称,但在此示例中为了简单而只使用的缺省包名。
术语表:
Bundle: 模块
Activator:启动器
Component:组件
Service:服务
Sevice Register:服务注册表
Declarative Service: 服务声明,简称DS
OSGi Framework: OSGi框架或者框架
manifest file: 元文件
=========================================================================================
OSGi开发起步(Getting Started with OSGi)-2与OSGi框架进行交互
原文出自: http://neilbartlett.name/blog/osgi-articles/ - Getting Started with OSGi
本文系翻译文章,但并未严格按照原文翻译。
2. 与OSGi框架进行交互
上一节举了一个简单的示例模块HelloWorld:启动和停止时输出消息的模块。该模块通过实现BundleActivator接口定义的start和stop函数方法完成了这些消息的输出。如果回过头来仔细的研究start和stop方法的定义,我们可以发现函数定义了一个BundleContext类型的参数。在本节描述参数BundleContext以及它的用法。
BundleContext是OSGi框架提供给一个模块的'通行票据'。当模块需要与框架进行交互和通信时,就可以使用BundleContext。实际上,这也是一个模块可以唯一与OSGi框架进行交互的渠道。OSGi框架在每个模块启动时会通过此模块的BundleActivator派送一个票据:BundleContext。
如果上一节的OSGi控制台还在运行状态,下面的工作就不需要重启控制台。如果控制台关闭了就需要使用下面的命令启动它。
> java -jar equinox.jar -console
输入ss后就会发现上次安装过的HelloWorld模块。即便是OSGi框架关闭重启了之后,HelloWorld模块也会保持上节的状态。这主要是因为OSGi框架进行了模块的持久化存储处理。
在这一节我们完成一个查找并卸载HelloWorld模块的模块。在OSGi控制台可以使用uninstall命令简单的完成这项任务。但是这一节希望通过OSGiAPI编码的方式实现此任务。
创建一个名称为HelloWorldKiller.java的文件,输入下面的代码:
import org.osgi.framework.*;
public class HelloWorldKiller implements BundleActivator {
public void start(BundleContext context) {
System.out.println("HelloWorldKiller searching...");
Bundle[] bundles = context.getBundles();
for(int i=0; i
if("HelloWorld".equals(bundles[i]
.getSymbolicName())) {
try { System.out.println("Hello World found, " + "destroying!"); bundles[i].uninstall(); return; } catch (BundleException e) { System.err.println("Failed: " + e.getMessage()); }
}
}
System.out.println("Hello World bundle not found");
}
public void stop(BundleContext context) { System.out.println("HelloWorldKiller shutting down"); }
}
接着创建此模块的元文件,注意文件最后要留一个空白行。在元文件中输入以下内容:
Manifest-Version: 1.0
Bundle-Name: HelloWorldKiller
Bundle-Activator: HelloWorldKiller
Bundle-SymbolicName: HelloWorldKiller
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
编译和构建模块对应的jar文件:
> javac -classpath equinox.jar HelloWorldKiller.java
>jar -cfm HelloWorldKiller.jar HelloWorldKiller.mf HelloWorldKiller.class
在控制台安装构建完成的新模块:install file:HelloWorldKiller.jar。然后输入ss。模块的状态类似以下列表:
id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919
1 ACTIVE HelloWorld_1.0.0
2 INSTALLED HelloWorldKiller_1.0.0
接着启动我们的新模块HelloWorldKiller:start 2。其输出信息为:
HelloWorldKiller searching...
Hello World found, destroying!
Goodbye Eclipse!
注意最后一行是先前那个HelloWorld模块的输出信息。当这个模块处于运行状态并且启动HelloWorldKiller时,HelloWorld模块被停止并进行了卸载。这种操作触发了HelloWorld模块启动器的stop方法。
输入命令ss就会发现HelloWorld模块消失了。
id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919
2 ACTIVE HelloWorldKiller_1.0.0
这里的问题是:安全性如何保证?好像任何模块都可以卸载其他模块。实际上OSGi定义了一个复杂的安全层次机制。这个安全机制提供了对模块与框架交互的精确控制。这样就可以把卸载模块的权限限制在某些特定的管理模块。重要的是OSGi进行安全控制基本上是一个配置过程,所以在这个讲解系列里我们关注点主要放在编码上。
在开始下节讲解之前,我们可以了解一下BundleContext接口,看看此接口提供了哪些方法可供使用。比如,我们可以使用installBundle方法安装一个模块到OSGi中。或者也可以获取当前所有安装的模块列表并打印这些模块最近一次修改的日期和时间。具体的方法可以参考OSGi R4的API javadoc .
术语表:
Bundle: 模块
Activator:启动器
Component:组件
Service:服务
Sevice Register:服务注册表
Declarative Service: 服务声明,简称DS
OSGi Framework: OSGi框架或者框架
manifest file: 元文件
=========================================================================
OSGi开发起步(Getting Started with OSGi)-3 模块间依赖
3. 模块间依赖
上两节展示了模块的启动和停止以及模块与系统框架的交互方式。但是问题是模块真正的用途是什么呢?
模块是功能集合。模块使我们能够把庞大纷杂的项目划分成可管理的单元。这些单元可以方便的载入到OSGi运行环境。问题是,不管我们喜欢还是不喜欢,模块几乎总是依赖于其他模块。对jar文件来说,从来没有一个可靠的方式来指定不同jar之间的依赖关系(注意,jar元文件中的类路径也不是一个可靠的方法)。因此你永远不能真正的确定: Jar中的代码会正常工作?还是会在运行时失控抛出一个ClassNotFoundException异常?
OSGi以简洁的方式解决了这个问题。仅仅这样说还不如通过功能展示来的更有说服力些。所以让我们马上开始编码来做到这点。
不幸的是直至现在,我们一直使用默认包的方式进行类的开发。这种方式不适应更复杂问题的编码,所以现在需要使用java包的形式进行编码开发和功能划分。首先以一个非常简单的JavaBean类开始ÿ