本文主要介绍如何创建一个最简单的 bundle?如何在 bundle 中监控服务的状态变化?如何配置 MANIFEST.MF 文件?如何编译、安装、运行我们打包好的 bundle,这并不一定需要是第一个例子,但是可以很好的帮助我们理解如何使用最简单的方式构建一个 bundle。
这个例子会详细的说明如何构建一个最简单的 bundle?如何编写相应的 manifest.mf 信息?如何编译、打包、安装?
每一个 bundle 可以通过全局唯一的 BundleContext 实例和 OSGI 框架交互,那我们应该如何在一个 bundle 中获取该实例呢?答案是我们的 Bundle 必须要实现 BundleActivator 接口,该接口包含 void start(BundleContext context) 和 void stop(BundleContext context) 两个方法,在 bundle 被安装,并且启动的时候,start 方法会被调用,此时 BundleContext 实例会以参数的形式传递进来,同样当框架被停止的时候,stop 方法会被调用,此时 BundleContext 实例会以参数的形式传递给用户,除了实现了 BundleActicator 接口,下面的例子中还实现了 ServiceListener 接口,目的是将 Bundle 作为订阅者,监听自身的状态的改变,具体实现如下:
/*
* Apache Felix OSGi tutorial.
**/
package tutorial.example1;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;
/**
* 这里我们实现了 OSGI 的服务事件监听机制,即实现了 ServiceListener,
* 在事件回调函数(ServiceChange)中,简单的输出了该事件的详细信息。
**/
public class Activator implements BundleActivator, ServiceListener
{
/**
* Implements BundleActivator.start(). Prints
* a message and adds itself to the bundle context as a service
* listener.
* @param context the framework context for the bundle.
**/
public void start(BundleContext context)
{
System.out.println("Starting to listen for service events.");
context.addServiceListener(this);
}
/**
* Implements BundleActivator.stop(). Prints
* a message and removes itself from the bundle context as a
* service listener.
* @param context the framework context for the bundle.
**/
public void stop(BundleContext context)
{
context.removeServiceListener(this);
System.out.println("Stopped listening for service events.");
// Note: It is not required that we remove the listener here,
// since the framework will do it automatically anyway.
}
/**
* Implements ServiceListener.serviceChanged().
* Prints the details of any service event from the framework.
* @param event the fired service event.
**/
public void serviceChanged(ServiceEvent event)
{
String[] objectClass = (String[])
event.getServiceReference().getProperty("objectClass");
if (event.getType() == ServiceEvent.REGISTERED)
{
System.out.println(
"Ex1: Service of type " + objectClass[0] + " registered.");
}
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
System.out.println(
"Ex1: Service of type " + objectClass[0] + " unregistered.");
}
else if (event.getType() == ServiceEvent.MODIFIED)
{
System.out.println(
"Ex1: Service of type " + objectClass[0] + " modified.");
}
}
}
以上类编写完成之后,我们还需要编写一个 manifest.mf 文件,这个文件的作用是声明 bundle 的一些列元信息,包括此 bundle 的名称、版本(重要)、创建者、启动入口(重要)、依赖的包(重要)、对外暴露的包(重要),具体如下:
Bundle-Name: Service listener example
Bundle-Description: A bundle that displays messages at startup and when service events occur
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tutorial.example1.Activator
Import-Package: org.osgi.framework // 必须以换行符结束(回车),否则此行将会被忽略。
Bundle-Name: Bundle 名称。
Bundle-Description: Bundle 的描述信息。
Bundle-Vendor: Bundle 的厂商信息,可能比较别扭,也就是这个 Bundle 是作者信息。
Bundle-Version: Bundle 的版本信息。
Bundle-Acticator: Bundle 的启动入口,当 Bundle 被安装之后,并且使用 Start 命令启动 Bundle 的时候,其指定的类会被自动构建,并且调用其实现的 BundleActivator 的 start 方法。
Import-Package: 声明此 Bundle 依赖的包,jre 标准提供的不需要声明。
Export-Package: 声明此 Bundle 中哪些包是对外暴露的,即允许其他 Bundle 使用。
接下来就是编译 Activator 类了,编译的时候,必须要保证 felix.jar 文件在类路径下,上述的文档中出现了中文,则需要在编译的时候指定源文件对应的编码格式,否则会编译出错,具体如下:
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo01\src> javac -cp ..\..\..\bin\felix.jar -encoding UTF-8 -d ../target .\tutorial\example1\*.java
上述的的命令如果看不明白的话,可以在命令行直接输入 javac,查看 javac 命令的使用帮助,这里我就不详细讲解了。
编译好 bundle 类之后,我们需要将编译好的类和 manifest.mf 一起打包为一个 jar 包,如下:
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo01\src> cd ../target
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo01\target> cp ../src/manifest.mf ./
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo01\target> jar cvfm example1.jar .\manifest.mf -C . .
已添加清单
正在添加: manifest.mf(输入 = 268) (输出 = 182)(压缩了 32%)
正在添加: tutorial/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example1/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example1/Activator.class(输入 = 1718) (输出 = 876)(压缩了 49%)
上述的的命令如果看不明白的话,可以在命令行直接输入 jar,查看 jar 命令的使用帮助,这里我就不详细讲解了。
打包完成之后我们将会得到一个名字为 example1.jar 的文件,然后再 felix 框架下安装此 bundle,具体命令如下:
Welcome to Apache Felix Gogo
g! install file:./examples/demo01/target/example1.jar
Bundle ID: 7
g! lb
START LEVEL 1
ID|State |Level|Name
0|Active | 0|System Bundle (6.0.0)|6.0.0
1|Active | 1|jansi (1.17.1)|1.17.1
2|Active | 1|JLine Bundle (3.7.0)|3.7.0
3|Active | 1|Apache Felix Bundle Repository (2.0.10)|2.0.10
4|Active | 1|Apache Felix Gogo Command (1.0.2)|1.0.2
5|Active | 1|Apache Felix Gogo JLine Shell (1.1.0)|1.1.0
6|Active | 1|Apache Felix Gogo Runtime (1.1.0)|1.1.0
7|Installed | 1|Service listener example (1.0.0)|1.0.0
g! start 7
Starting to listen for service events. // bundle 启动输出信息
当上面的 bundle 被安装启动之后,框架中所有的服务注册、注销、更改事件都会被此 bundle 检测到,并且会打印出相应的事件信息,再后面的例子中我们将会讲解 OSGI 框架中服务如何构建、注册、使用。