osgi开发,
-在OSGi框架运行时环境中,包的运行时上下文。如果一个Bundle定义了一个BundleActivator接口的实现类,并在Bundle列表[Bundle-Activator]中指明了Activator,那么在Bundle启动和停止时,该Bundle的运行时上下文将通过BundleActivator传递到OSGi框架中。
-org . osgi . framework . Bundle activator接口的一个实现类,OSGi框架在启动和停止这个bundle的时候会调用这个类的start和stop方法。
-描述捆绑包生命周期操作的事件。当包的生命周期状态改变时(比如包启动),所有包的生命周期事件将被同步发送到所有生命周期事件监听器。
-在服务注册表中注册一个Java对象。该对象实现一个或多个接口。通过这些接口和绑定到接口的属性列表,该对象提供由接口定义的服务。这个对象可以通过接口名称和属性被其他Bundle发现和使用。
-对服务的引用。该引用仅用于访问被引用服务的属性,而不是真正的服务对象。的实际服务对象必须通过Bundle的Bundle上下文获取。
3.捆绑描述列表
每个包都有一组描述其自身信息的元数据清单。这个清单以名称-值对的头信息的形式描述了包的所有方面,比如包的名称(Bundle -Name:acme)、包的版本(Bundle-version: 1.1)等。这个清单存储在META- INF/MANIFEST中。包中的MF文件。OSGi定义了一系列标准列表如下:
捆绑包激活器:com.acme.fw.Activator
这个头信息指示OSGi框架在启动和停止这个包时调用的BundleActivator接口的类实现。
bundle-DocURL:http:/www . acme . com/Firewall/doc
URL指示该包的文档信息的位置。
dynamic import-Package:com . acme . plugin . *
这个包动态引用其他包发布的包,多个包之间用逗号分隔。
导出-包:org . OSGi . util . tracker;版本=1.3
这个头信息可以发布包中的类包。
片段-主机:org . eclipse . swt;bundle-version=[3.0.0,4 . 0 . 0]
如果包是片段类型的,则头部信息用于描述包的主机包。
import-Package:org . OSGi . util . tracker,org . OSGi . service . io;版本=1.4
Bundle引用其他Bundle发布的包,多个引用之间用逗号分隔。
注意:请参考OSGi R4.1核心规范,了解捆绑包元数据清单的详细信息。
4.捆绑的概念
在OSGi环境中,Bundle可以认为有两种含义,即静态Bundle和运行时Bundle。
4.1.静态束
理论上,静态束可以以任何形式存在。在Equinox OSGi的实现中,静态包由一组资源文件组成,它们被表示为文件系统目录或打包的JAR文件。如下图所示,用户为Bundle资源创建一个文件目录,将META-INF目录添加到这个目录中,生成MANIFEST。MF文件。
在清单上。MF文件,我们可以定义包的元数据头信息。比如包的名称(Bundle-Name)、包的唯一标识符(Bundle-SymbolicName)以及包所依赖的其他包组件(required-Bundles)。用户还可以定义自己的头信息,如Acme-ID:1213等。OSGi框架解析元数据列表时会忽略用户自己定义的头信息,需要用户自己解析。
用户可以将Bundle的资源添加到Bundle目录中,比如定义Bundle中这些类引用的类包(com.acme.*)和其他类资源(xml- api.jar,JUnit . jar);用户还可以将这个包中的类包打包到JAR文件中,比如runtime.jar.
捆绑组件不必发布Java类资源。如果一个Bundle组件没有发布任何Java类资源,那么这个Bundle就可以看作一个资源包,Bundle中的资源可以被其他Bundle使用。比如用OSGi实现JBI(JSR-208)ESB系统时,包含Java类资源定义的Bundle可以实现JBI框架和内部服务引擎(SE)以及绑定组件(BC);没有Java类定义的Bundle工件可以用来描述为SE和BC定义的配置,即SU(服务单元)或SA(服务组装)。
每个Bundle组件都有自己的类空间,它自己在Bundle内部的类空间可以用元数据头Bundle-Classpath来描述,比如bundle-classpath:runtime.jar、lib/XML-API.jar、lib/JUnit.jar。
从上面的描述可以看出,只要准备好必要的资源和捆绑元数据manifest (MANIFEST。MF)文件,就可以构建这个包了。那么,这些捆绑组件在实际系统中会起到什么作用呢?
先来回顾一下通常的应用开发模式:简而言之,在获取用户需求后,我们对用户需求进行分析以确定系统功能,为系统功能抽象出对象模型,用Java类描述对象模型来实现这些系统功能。在这样的系统中,我们的系统实现代码是紧密集成的。当系统的一个需求改变时,我们必须重新定义相关的类并重新编译整个系统。如果在系统实现的过程中很好地定义了模块化的程度,那么只需要重新编译和发布某个模块。即便如此,维护这样的系统也是非常复杂的,尤其是随着系统功能的不断扩展,系统越来越庞大,维护成本也越来越高。
显而易见,捆绑组件的开发模式在解决上述问题时会给我们带来好处。我们还是要从系统需求分析入手,确定系统的功能,划分系统的功能模块。对于每个功能模块,我们构建面向对象的模型,并使用Java类来实现这些模型。但是,在实现的过程中,我们的视角发生了一定程度的变化。对于每一个功能模块,我们需要明确该模块向外部系统或内部系统的其他模块发出什么样的操作接口;然后,我们只需要通过Bundle的Export-Package机制,发布这些接口的定义和门户来操作这些接口的功能实现,屏蔽这些接口操作的内部实现;其他模块只有通过导入-封装这些接口定义和功能实现入口,才能使用本模块的操作与本模块进行交互。当一个功能模块的需求发生变化时,如果接口操作不变,只改变内部实现,对整个系统没有任何影响。如果接口操作发生变化,我们还可以发布模块的更高版本实现,不同版本可以在同一个出货环境。通过这种发布、引用、阻塞和版本控制机制,可以实现一个高度松耦合的系统,系统实现可以不受限制地进行一定程度的扩展。
4.2动态捆绑包
OSGi框架启动后,它将加载捆绑包的静态资源,并构建相应的运行时捆绑包实例。OSGi框架为每个运行时捆绑包分配一个捆绑包上下文,每个捆绑包可以利用这个上下文获取OSGi框架提供的各种信息和功能,比如框架内部发布的事件,其他捆绑包分发的服务等。仅当包在元数据清单(manifest)中定义了包激活器头信息时。MF),并且OSGi框架启动和停止捆绑包,则捆绑包上下文通过实现由捆绑包激活器头部信息指定的BundleActivator接口的类被传递到捆绑包上下文中。由Bundle-Activator定义的BundleActivator示例如下:
公共类MyBundleActivator实现org . OSGi . framework . bundleactivator {
公共void start(BundleContext上下文)引发异常{
}
公共void停止(BundleContext上下文)引发异常{
}
}
如果用户构建的Bundle需要获取OSGi的上下文,用户必须为Bundle定义Bundle activator的实现,并在Bundle-Activator中指明。通常,捆绑包可以分为三类:
仅提供捆绑包;的资源;
为其他bundle提供工具类的bundle,类似于为其他类提供公共操作的工具类;
为其他包提供服务的包,以及由其他包发布的参考服务。我们将在下面的例子中详细介绍OSGi的服务。
5.构建包的步骤
在熟悉了Bundle的上述含义后,我们可以根据Bundle的功能采用不同的开发步骤。
如果我们需要开发一个资源类的捆绑包,那么我们只需要准备好所需的资源,并在捆绑包列表中定义捆绑包的唯一标识符和其他必要的信息。
如果我们需要开发一个工具类的捆绑包,而这些工具类不需要访问OSGi环境信息,那么我们只需要定义捆绑包列表,实现捆绑包提供的工具类,并在捆绑包列表中导出打包这些工具类。
如果我们需要开发一个工具类的捆绑包,这些工具类需要访问OSGi的环境信息,那么我们需要定义一个实现BundleActivator接口的类,在Bundle-Activator中指明这个类,同时定义所有的工具类(工具类可以访问Bundle Activator实现类中接收的OSGi框架交付的BundleContext),然后发布这些工具类。
如果我们需要开发一个提供服务和/或引用服务的捆绑包,那么我们必须首先定义一个BundleActivator的实现类,获取这个类中的BundleContext,通过这个Context向OSGi环境注册这个捆绑包发布的服务和/或找到捆绑包发布的其他服务并引用它们。
6.管束结构示例
在这个例子中,我们定义了一个简单的应用系统,它监控一个指定的目录,当目录中有符合设定的文件过滤规则的文件时,它获取这些文件的内容,并将其作为事件发布。应用系统在运行过程中需要记录运行日志,当系统运行异常时,便于维护人员确定异常原因。
在确定了上述需求之后,通过分析可以得出结论,除了核心业务功能之外,我们还需要为系统开发日志功能和事件发布功能。为了示例的清晰,我们定义了以下包:
日志服务包:这个包发布日志服务,
事件发布服务包:这个包发布事件服务。
目录监视包:这个包监视指定的目录,读取符合过滤规则的文件内容,并发布事件。
文件处理包:这个包监听文件目录,监控包发布的事件,并处理它们。
6.1日志服务包
首先,我们需要定义日志服务的ILogService接口:
包com . example . log;
公共接口ILogService {
void debug(字符串消息);
void debug(字符串消息,Throwable t);
void info(字符串消息);
void info(字符串消息,Throwable t);
void错误(字符串消息);
void错误(字符串消息,Throwable t);
}
然后,我们定义ILogService接口的实现,该接口将日志输出到控制台:
包com . example . log . internal;
导入Java . io . bytearrayoutputstream;
导入Java . io . printstream;
导入Java . text . date format;
导入Java . util . date;
导入com . example . log . ilogservice;
公共类LogServieImpl实现ILogService {
公共void调试(字符串消息){
日志([调试],消息);
}
公共void调试(字符串消息,Throwable t) {
log([DEBUG],message,t);
}
公共void错误(字符串消息){
日志([错误],消息);
}
公共void错误(字符串消息,Throwable t) {
日志([错误],消息,t);
}
公共无效信息(字符串消息){
日志([信息],消息);
}
公共void信息(字符串消息,Throwable t) {
log([INFO],message,t);
}
私有void日志(字符串级别,字符串消息){
日志(级别、消息、空);
}
私有void日志(字符串级别,字符串消息,Throwable t) {
string buffer log entry=new string buffer();
日志条目。追加(日期格式。getdatetime实例().format(new Date()));
日志条目。追加(" ");
logEntry.append(级别);
日志条目。追加(" ");
logEntry.append(消息);
日志条目。追加(" ");
如果(t!=null) {
日志条目。append(getStackTraceAsString(t));
}
if(t==null)
系统。出去。println(日志条目);
其他
系统。呃。println(日志条目);
}
私有字符串getStackTraceAsString(Throwable t){
字符串retString=null
if (t==null) {
返回retString
}
尝试{
ByteArrayOutputStream out stream=new ByteArrayOutputStream();
PrintStream PrintStream=new PrintStream(out stream);
if (t.getStackTrace()!=null) {
t。printstacktrace(printStream);
retString=out stream。tostring();
printstream。close();
}否则{
返回t . getmessage();
}
逆流而上。close();
} catch(异常e) {
}
返回retString
}
}
在定义了日志记录服务接口及实现后,我们需要将日志记录服务发布出去供其他捆引用。为此,我们需要定义BundleActivator接口的实现,获取无效停止向开放服务网关协议环境中注册该日志服务BundleActivator接口的实现类如下:
包com。举例。日志。内部;
导入Java。util。哈希表;
导入org。OSGi。框架。束激活器;
导入org。OSGi。框架。捆绑上下文;
导入org。OSGi。框架。常数;
导入com。举例。日志。ilogservice
公共类激活器实现BundleActivator
公共void开始(BundleContext上下文)引发异常{
//生成日志记录服务的实例
logserveimpl log impl=new logserveimpl();
//设定日志记录服务的属性
散列表
//注册日志记录服务
语境。注册服务(ilogservice。班级。getname(),
logImpl,properties);
}
公共空的停止(联邦上下文上下文)引发异常{
}
}
在完成日志记录服务的所有类定义后,我们需要为该捆编写元数据清单。在该捆中,我们只需要向其他捆发布日志服务接口包即可,其他捆不必了解日志记录服务是如何实现的,因此,我们在出口包装头信息中只输出:com.example.log包。该捆的元数据清单如下:
清单-版本:1.0
捆绑包-清单版本:2
捆绑包名称:日志插件
bundle-符号名:com。举例。原木
捆绑版本:1.0.0
捆绑激活器:com。举例。日志。内部。催化剂
捆绑包供应商:示例
导入-包:org。OSGi。框架;版本=1.3.0
Export-Package: com.example.log
至此,日志记录服务捆已经开发完成。
6.2 事件管理服务
对于一个事件管理服务来说,用户使用该服务必须能够定义自己的事件,定义事件的监听处理。在本示例中,事件定义我们直接使用java.util.EventObject表示,用户可以继承此类定义自己的事件。
首先,我们定义事件监听处理伊文钱德勒接口:
包com。举例。事件;
导入Java。util。事件对象;
公共接口IEventHandler {
void句柄事件(事件对象事件);
}
然后,我们定义事件发布管理IEventManager接口:
包com。举例。事件;
导入Java。util。事件对象;
公共接口IEventManager {
void fireEvent(事件对象事件);
public void registEventHandler(IEventHandler处理程序);
}
现在我们来定义事件发布管理接口的实现,与日志记录服务相似,我们不需要将该实现暴露给其他捆绑,因此我们使用内部的类包:
包com。举例。事件。内部;
导入Java。util。ArrayList
导入Java。util。事件对象;
导入com。举例。事件。ieventhandler
导入com。举例。事件。ieventmanager
公共类EventManagerImpl实现IEventManager {
公共数组列表处理程序列表=新数组列表(
7);
公共void fireEvent(事件对象事件){
对于(IEventHandler处理程序:handlerList) {
handler.handlEvent(事件);
}
}
public void registEventHandler(IEventHandler处理程序){
handlerList.add(处理程序);
}
}
现在,我们定义该捆的BundleActivator实现,注册事件管理服务:
包com。举例。事件。内部;
导入Java。util。哈希表;
导入org。OSGi。框架。束激活器;
导入org。OSGi。框架。捆绑上下文;
导入org。OSGi。框架。常数;
导入com。举例。事件。ieventmanager
公共类激活器实现BundleActivator
公共void开始(BundleContext上下文)引发异常{
//生成事件管理服务实例
EventManagerImpl em=new EventManagerImpl();
//设定事件管理服务的属性
散列表
//注册事件管理服务
语境。registerservice(ieventmanager。班级。getname()、em、properties);
}
公共空的停止(联邦上下文上下文)引发异常{
}
}
最后,我们定义事件服务捆的元数据清单:
清单-版本:1.0
捆绑包-清单版本:2
捆绑包名称:事件插件
bundle-符号名:com。举例。事件
捆绑版本:1.0.0
捆绑激活器:com。举例。事件。内部。催化剂
捆绑包供应商:示例
导入-包:org。OSGi。框架;版本=1.3.0
导出包:com.example.event
至此,事件管理服务开发完毕。
6.3 文件目录监控捆
在此示例中,该捆启动时获取日志记录服务和事件管理服务,查找系统属性file.monitor.dir 和文件。监视器。过滤器,确定监控目录及监控规则,并开始监控。当监控目录下存在符合过滤条件的文件时,读取该文件的内容发布文件监控事件事件。
我们首先定义FileMonintorEvent类,该类继承java.util.EventObject类,用户可以从该类中获取监控到的文件。该类定义如下:
包com。举例。文件。监视器;
导入Java。io。文件;
导入Java。util。事件对象;
公共类文件监控事件扩展事件对象{
私有文件m _文件
公共文件监控事件(文件文件){
超级(文件);
m _ file=文件
}
公共文件getFile() {
返回m _文件
}
}
下面,我们定义文件监控服务类,该类使用ILogService和IEventManager服务,开启一个新的线程监控指定的目录。如果用户未设定文件。监视器。目录属性,默认监控目录为c:/OSGi _测试;如果用户未设定文件。监视器。过滤器属性,默认监控文件名为数字,后缀为文本文件(文本文件)的文件,如1212.txt。该类的实现如下:
包com。举例。文件。监视器。内部;
导入Java。io。文件;
导入Java。io。文件名过滤器;
导入Java。util。正则表达式。matcher
导入Java。util。正则表达式。图案;
导入com。举例。事件。ieventmanager
导入com。举例。文件。监视器。filemonitorevent
导入com。举例。日志。ilogservice
公共类FileMonitorService {
专用ILogService日志;
私有IEventManager em
私有字符串目录;
私有字符串筛选器;
私有文件监视器目录;
protected boolean doMonitor=true;
公共FileMonitorService(ILogService日志服务,IEventManager事件管理器){
log=日志服务
em=事件管理器
}
public void start() {
字符串m _ dir=system。getproperty(文件。监视器。dir’);
if (m_dir!=null m_dir.length() 0) {
目录=m _目录
}否则{
dir= c://OSGi _ test ;
文件f=新文件(dir);
如果(!f.exists()) {
if (f.mkdir())
log.info(无法创建默认的监视器目录[c://OSGi _测试]);
}否则
log.info(属性[文件.监视器.目录]未赋值,使用默认值[c://OSGi _测试]);
}
字符串m _ filter=system。getproperty(文件。监视器。过滤器’);
if (m_filter!=null m_filter.length() 0) {
过滤器=m _过滤器
}否则{
filter=//d . txt ;
}
log.info(文件监控过滤规则为[ filter ]);
最终匹配器matcher=Pattern.compile(filter).匹配器("");
最终文件名过滤器文件名过滤器=新文件名过滤器(){
公共布尔接受(文件目录,字符串名称){
返回matcher.reset(名称).匹配();
}
};
监控目录=新文件(dir);
如果(!monitorDir.exists()) {
log.info(监视器目录[ dir ]不存在!);
返回;
}
新线程(文件监视器){
公共无效运行(){
while (doMonitor ) {
file[]files=monitor目录。列出文件(文件名过滤器);
对于(文件文件:文件){
log.debug(文件监控服务查找文件[
文件。getname()]);
FileMonitorEvent event=new FileMonitorEvent(file);
em.fireEvent(事件);
}
尝试{
线程。睡眠(10000);
} catch (InterruptedException e) {
//
}
}
}
}.start();
}
公共无效站点(){
doMonitor=false
}
}
然后,我们定义该捆的活化剂:
包com。举例。文件。监视器。内部;
导入org。OSGi。框架。束激活器;
导入org。OSGi。框架。捆绑上下文;
导入org。OSGi。util。追踪器。服务跟踪器;
导入com。举例。事件。ieventmanager
导入com。举例。日志。ilogservice
公共类激活器实现BundleActivator
私有ServiceTracker日志跟踪器
私有ServiceTracker emTracker
私有静态ILogService日志;
私有静态IEventManager em
私有文件监控服务对外军贸(foreign military sales的缩写)
公共void开始(BundleContext上下文)引发异常{
log tracker=new service tracker(context,ILogService.class.getName(),
null);
日志跟踪器。open();
log=(ILogService)日志跟踪器。获取服务();
em tracker=新服务跟踪器(context,IEventManager.class.getName(),
null);
电磁跟踪器。open();
em=(IEventManager)em跟踪器。获取服务();
fms=new FileMonitorService(log,em);
FMS。start();
}
公共空的停止(联邦上下文上下文)引发异常{
如果(fms!=空)
FMS。stop();
fms=空
if (logTracker!=空)
日志跟踪器。close();
logTracker=null
log=null
if (emTracker!=空)
电磁跟踪器。close();
emTracker=null
em=null
}
公共静态ILogService getLogService() {
返回日志;
}
公共静态IEventManager getEventManager(){
返回em;
}
}
最后,我们定义文件监控服务捆的元数据清单:
清单-版本:1.0
捆绑包-清单版本:2
捆绑包名称:监视器插件
bundle-符号名:com。举例。文件。班长
捆绑版本:1.0.0
捆绑激活器:com。举例。文件。监视器。内部。催化剂
捆绑包供应商:示例
import-Package:com举例。事件
com.example.log,
org。OSGi。框架;版本=1.3.0 ,
org。OSGi。util。追踪器;版本=1.3.3
导出包:com.example.file.monitor
至此,文件监控目录服务完成实现。
6.4 文件监控事件处理捆
此捆监听文件目录监控服务捆发布的文件监控事件,并处理该事件,为了简化,我们将捆的催化剂实现作为文件监控事件的处理器,类定义如下:
包com。举例。文件。监视器。处理者;
导入Java。util。事件对象;
导入org。OSGi。框架。束激活器;
导入org。OSGi。框架。捆绑上下文;
导入org。OSGi。util。追踪器。服务跟踪器;
导入com。举例。事件。ieventhandler
导入com。举例。事件。ieventmanager
导入com。举例。文件。监视器。filemonitorevent
导入com。举例。日志。ilogservice
公共类激活器实现BundleActivator,IEventHandler {
私有ServiceTracker日志跟踪器
私有ServiceTracker emTracker
私有静态ILogService日志;
私有静态IEventManager em
公共void开始(BundleContext上下文)引发异常{
log tracker=new service tracker(context,ILogService.class.getName(),
null);
日志跟踪器。open();
log=(ILogService)日志跟踪器。获取服务();
em tracker=新服务跟踪器(context,IEventManager.class.getName(),
null);
电磁跟踪器。open();
em=(IEventManager)em跟踪器。获取服务();
em.registEventHandler(这个);
}
公共空的停止(联邦上下文上下文)引发异常{
if (logTracker!=空)
日志跟踪器。close();
logTracker=null
log=null
if (emTracker!=空)
电磁跟踪器。close();
emTracker=null
em=null
}
公共void句柄事件(事件对象事件){
文件监控事件的事件实例){
FileMonitorEvent FM=(FileMonitorEvent)事件;
如果(日志!=空)
log.info(接收文件监视器事件,文件名为[
. getFile().getName()]);
}
}
}
该捆的元数据清单如下:
清单-版本:1.0
捆绑包-清单版本:2
捆绑包名称:处理器插件
bundle-符号名:com。举例。文件。监视器。处理者
捆绑版本:1.0.0
捆绑激活器:com。举例。文件。监视器。处理程序。催化剂
捆绑包供应商:示例
import-Package:com举例。事件
com.example.log,
org。OSGi。框架;版本=1.3.0 ,
org。OSGi。util。追踪器;版本=1.3.3
require-Bundle:com举例。文件。班长
至此,文件监控事件处理捆定义完成。
6.5小结
上面的例子是用最简单的方式实现的,只是用来展示OSGi Bundle在创建高度松散耦合的应用系统方面的优越性。这里我们没有涉及实现和微妙控制的细节。
下图显示了Eclipse中上述示例的项目实现。点击此处下载。
用户可以直接在Eclipse的OSGi开发环境中运行上述示例,或者将它们打包发布到独立的OSGi运行环境中。关于OSGi运行环境,请参考上述文件。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。