该规范和核心部分是一个 ,当中定义了应用程序的模式和。基于这个框架定义了大量的OSGi服务: 、、偏好,(执行)、分析、设备訪问、管理、许可管理、星级、用户管理、IO连接、连线管理、和 。
这个框架实现了一个优雅、完整和动态的。应用程序(称为)无需又一次引导能够被远程安装、启动、升级和卸载(当中Java包/类的管理被具体定义)。
API中还定义了执行远程下载管理政策的。同意bundles去检測新和取消的服务。然后对应配合。
OSGi原先关注于。事实上可用于多个方面。如今OSGi规范已经用于从到开源的(当中包含了与IBM的OSGi框架兼容的开源版本号)。 OSGi服务平台的应用包含:、 、、 、、 、(如)、和 。
OSGi规范是由成员通过公开的程序开发,对公众免费而且没有许可证限制。
可是OSGi Alliance的程序仅仅对成员开放,眼下有12个兼容的。
2003年选择OSGi作为其的底层执行时架构。 project对该理念进行了实验。2004年6月在Eclipse3 R3中公布。是面向OSGi开发人员的Eclipse插件。
OSGI 里面有几个核心类:BundleContext, Bundle, ServiceRegistration, ServiceReference
关于OSGI的启动包含一个启动jar包和一个配置文件
Equinox\Configuration\Config.ini中放置的是启动信息
eclipse.ignoreApp=true
osgi.noShutdown=true
osgi.framework.activeThreadType=normal
Equinox\org.eclipse.osgi_3.5.0.v20090520.jar
假设将该Jar放在在C盘osgi-dev下,那么在命令提示符下进入C:/osgi-dev 文件夹,执行以下命令:
> java -console -jar org.eclipse.osgi_3.3.1.R33x_v20070828.jar
几秒钟,osgi>提示符显示出来。
以下为一个sh脚本启动Osgi:
#!/bin/sh
#resl=`ps -ef|grep yuewang|grep -v grep`
resl=`ps -ef|grep org.eclipse.osgi_3.5.0.v20090520.jar|grep -v grep`
flag=0
for i in $resl
do
#echo $i
if [ ! -z $i ]; then
flag=1
fi
done
if [ $flag == 0 ]; then
echo "starting SDMExpert"
cd /etc/sdme
java -Dlog4j.configuration=file:///etc/sdme/Properties/log4j.xml -DSDMExpert.corbaTrace=off
-Djacorb.connection.client.idle_timeout=600000 -Djacorb.connection.client.pending_reply_timeout=300000
-Djacorb.poa.queue_wait=on -Djacorb.net.socket_factory.port.max=3000
-Dlogconsole -jar /usr/sdme/bin/org.eclipse.osgi_3.5.0.v20090520.jar
-configuration /local/sdme/equinox -clean &
echo "SDMExpert started now"
else
echo " sdmexpert already started"
fi
exit
可执行Java包
OSGi 规范简介
OSGi 联盟建立于 1999 年,是一个非赢利机构。旨在建立一个开放的服务规范。
OSGi 规范为网络服务定义了一个标准的、面向组件的计算环境,它最初的目的就是为各种嵌入式设备提供通用的软件执行平台。屏蔽设备操作系统与硬件差别的中间件平台。通过这个平台。能够对不同软件商提供的应用(OSGi 中称为 Bundle)进行组件的生命周期管理的能力,如应用组件能够从执行中被安装、升级或者移除而不须要中断设备的操作,应用组件能够动态的发现和使用其它库或者应用程序。
因为 OSGi 技术具有服务组件模块化、动态载入应用等长处,正被越来越多的领域关注,如嵌入设备制造业、汽车制造业、企业应用等。眼下,OSGi 联盟公布的最新的 OSGi 服务规范为 4.0。读者能够查阅參考资料了解具体信息。
OSGi 体系结构
OSGi 的体系架构是基于插件式的软件结构,包含一个 OSGi 框架和一系列插件。在 OSGi中,插件称为 Bundle,当中,OSGi 框架规范是 OSGi 规范的核心部分,它提供了一个通用的、安全可管理的 Java 框架,通过这个框架。能够支持 Bundle 服务应用的部署和扩展。Bundle 之间能够通过 Import Package 和 Require-Bundle 来共享 Java 类。在 OSGi 服务平台中。用户通过开发 Bundle 来提供须要的功能。这些 Bundle 能够动态载入和卸载,或者依据须要远程下载和升级。OSGi 体系结构图如图 1 所看到的:
图示1 OSGi 体系结构当中:
Execution Environment:
Bundle 应用所倚赖执行的 Java 执行环境。如 J2SE-1.4、CDC-1.0 等都是可用的执行环境。
Modules:
模块层定义了 Bundle 应用的载入策略。OSGi 框架是一个健壮而且严格定义的类载入模型。在大多数 Java 应用中。通常仅仅有一个单独的 ClassPath,它包含了全部的 Java 类文件和资源文件。OSGi基于Java技术。对于每一个实现了 BundleActivator 接口的 Bundle 应用,为它生成一个单独的 ClassLoader,使得 Bundle 应用的组织更加模块化。
Life Cycle:
生命周期层能够动态地对 Bundle 进行安装(install)、启动(start)、停止(stop)、升级和卸载等操作。该层基于模块层。提供了一组 API 来控制 Bundle 应用的执行时操作。
Registers the specified service object with the specified properties under the specified class names into the Framework. A ServiceRegistration object is returned. The ServiceRegistration object is for the private use of the bundle registering the service and should not be shared with other bundles. The registering bundle is defined to be the context bundle. Other bundles can locate the service by using either the
{getServiceReference} method.
The ServiceRegistration object may be used to update the properties of the service or to unregister the service.
Service Registry 和 Services:
servR = context.registerService(IBundleInstallerService.class.getName(), installer, null);
servR.unregister();
为啥要使用ServiceRegistration和ServiceReference,我的观点是统一的接口进行管理和为了性能。使用Reference而不是对象本身,方面能够节省内存一方面能够提高性能。
BundleContext.registerService得到ServiceRegistration,通过ServiceRegistration的getReference得到ServiceReference。通过ServiceReference的getService得到对象本身。
注冊时使用的Properties文件的Properties信息来源于bundle中的Manifest.xml文件。
OSGi 服务层定义了一个集成在生命周期层中的动态协作模型,是一个公布、动态寻找、绑定的服务模型。一个服务一般是一个 Java 对象实现了特定的服务接口,而且通过服务注冊,被绑定到 OSGi 的执行环境中。
Bundle 应用能够注冊公布服务。动态绑定服务,而且在服务注冊状态改变时,能够接受到事件消息等。
Security:
OSGi 的安全管理是基于 Java2 安全体系的,贯穿在 OSGi 平台的全部层中。它能够对部署在 OSGi 执行环境中的 Bundle 应用进行具体的管理控制。
Bundle 生命周期的状态
在一个动态扩展的 OSGi 环境中,OSGi 框架管理 Bundle 的安装和更新。同一时候也管理 Bundle 和服务之间的依赖关系。
一个 Bundle 可能处于以下六个状态,关于Bundle的生命周期能够通过Bundle.getState()方法得到。如图 2 所看到的:
图示 2 Bundle 状态图INSTALLED:安装完毕,本地资源成功载入: BundleContext.installBundle(location)方法,成功后返回一个bundle对象。
RESOLVED:依赖关系包含classpath及package满足。这个状态意味该Bundle要么已经准备好执行,要么是被停止了。体如今代码上例如以下:通过getBundlesRequirement方法得到全部bundle的BundleRequirement,这种方法中主要是对Require-Bundle里面的全部Bundle进行解析放到List中。再通过matchBundleAndRequirement对Bundle的依赖进行验证,满足后Bundle的状态就变为Resolved状态了。
private HashMap<Bundle, ArrayList<BundleRequirement>> getBundlesRequirement(ArrayList<Bundle> bundles) {
HashMap<Bundle, ArrayList<BundleRequirement>> bundleTree = new LinkedHashMap<Bundle, ArrayList<BundleRequirement>>();
for (Bundle bdl : bundles) {
ArrayList<BundleRequirement> req = BundleUtil.getRequirement(bdl);
bundleTree.put(bdl, req);
}
return bundleTree;
}
public HashMap<Bundle, ArrayList<Bundle>> matchBundleAndRequirement(HashMap<Bundle, ArrayList<BundleRequirement>> bundlesRequirement) {
HashMap<Bundle, ArrayList<Bundle>> bundleTree = new HashMap<Bundle, ArrayList<Bundle>>();
for (Bundle key : bundlesRequirement.keySet()) {
ArrayList<BundleRequirement> requirement = bundlesRequirement.get(key);
ArrayList<Bundle> list = new ArrayList<Bundle>();
for (BundleRequirement req : requirement) {
for (Bundle candidate : bundlesRequirement.keySet()) {
if (req.satisfyRequirement(candidate)) {
list.add(candidate);
}
}
}
bundleTree.put(key, list);
}
return bundleTree;
}
STARTING:Bundle正在被启动,BundleActivator的start()方法已经被调用可是还没有返回。
STOPPING:Bundle正在被停止,BundleActivator的stop()方法已经被调用可是还没有返回。
ACTIVE:Bundle 被成功启动而且在执行。
UNINSTALLED:bundle被卸载而且无法进入其它状态。
Bundle接口定义了getState()方法来返回Bundle的状态。
OSGi 标准服务
在 OSGi 平台之上。OSGi 联盟定义了非常多服务。服务是由一个 Java Interface 来定义的。Bundle 能够实现这个接口而且把服务注冊到服务注冊表中去,用户能够从注冊表中找到须要的服务来使用,而且能够响应特定服务的状态改变,如服务注冊和服务取消。以下简介一下 OSGi Release 4 的一些主要服务。OSGi 框架提供了权限管理服务,包管理服务和最初载入系统服务。这些服务是 OSGi 框架的一部分而且管理着 OSGi 框架的运作。
Permission Admin Service:权限管理是指 Bundle 是否许可其它的 Bundle 的代码。
当前的或者其它的 Bundle 的权限能够通过这个服务来操作,一旦被设定权限,立即就生效。
Package Admin Service:Bundle 之间能够共享包内的 Java 类和资源,bundle 的更新可能须要 OSGi 框架又一次解析 Bundle 之间的依赖关系,这个服务提供了 OSGi 服务平台中包的共享状态信息。
Start Level Service:Start Level是指一些在特定Bundle起动之前必须执行或者初始化的一系列 bundle。Start Lever Service 能够设置当前OSGi服务框架初始的Start Level。而且能够指定和查询特定Bundle的Start Level。
使用 Eclipse 开发 Bundle 应用
Equinox 框架是 Eclipse 组织基于 OSGi Release 4 的一个实现框架,它实现了 OSGi 规范的核心框架和很多标准框架服务的实现。关于Equinox项目的具体信息,请查阅參考资料信息。在本文中,我们使用 Eclipse 3.2 平台开发两个基于 OSGi 的 Bundle 应用。当中第一个 Bundle 应用声明、实现并注冊了一个姓名查询服务,用于推断所给姓名是否在已定义的查询列表中;第二个 Bundle 应用查询并引用第一个 Bundle 应用所注冊的姓名查询服务,假设用户所给的姓名包含在查询列表中,将返回正确的信息。最后。将开发的 Bundle应用部署的 Equinox OSGi 框架中。用户能够在 OSGi 控制命令行中输入命令来查询关于框架和 Bundle 应用的具体信息。读者能够从參考资料中获得本文 Bundle 应用的源码。
(1)创建 Plug-in Project,在 Eclipse 3.2 开发环境中,从菜单条选择 File > New > Project... ,打开 New Project 向导,能够看到有Plug-in Project创建向导。创建一个Plug-in 项目。项目名为 example 的 Bundle 应用,该应用实现并注冊了一个姓名查询服务,实现了 BundleActivator 接口。选择 Equinox 框架作为 Bundle 应用执行的 OSGi 服务平台。
图示 3 Plug-in 项目向导(2)实现OSGi服务通常须要两个步骤。首先定义所提供服务的接口,然后实现这个服务接口。在本例中,我们创建一个姓名查询服务用来查询所给姓名是否有效。首先定义姓名查询接口NameService.java。以下是该接口的源码:
NameService Interface 源码 package example.service; /** * A simple service interface that defines a name service. * A name service simply verifies the existence of a Name. **/ public interface NameService { /** * Check for the existence of a Name. * @param name the Name to be checked. * @return true if the Name is in the list, * false otherwise. **/ public boolean checkName(String name); } |
该服务接口非常easy,仅仅包含一个须要实现的方法。
为了将服务接口和服务实现相分离,方便其它 Bundle 引用该服务。我们通常须要将该服务接口单独放在一个包内,本例中,存放NameService.java 接口的 Java 包为 example.service。接下来,须要实现 NameService 接口,而且注冊该服务。在本例中,我们用内部类实现了该接口,以下是该 Bundle 应用的部分源码。
Example Bundle部分源码 public void start(BundleContext context) throws Exception { Properties props = new Properties(); props.put("ClassRoom", "ClassOne"); context.registerService(NameService.class.getName(), new NameImpl(), props); } private static class NameImpl implements NameService { // The set of names contained in the arrays. String[] m_name = { "Marry", "John", "David", "Rachel", "Ross" }; /** * Implements NameService.checkName(). Determines if the passed name is * contained in the Array. * * @param name * the name to be checked. * @return true if the name is in the Array, false otherwise. */ public boolean checkName(String name) { // This is very inefficient for (int i = 0; i < m_name.length; i++) { if (m_name[i].equals(name)) { return true; } } return false; } } |
在start()方法中,利用BundleContext注冊一个姓名查询服务,而且为该服务设置相关属性以便服务查询。在实现姓名查询服务时。我们简单定义了一个静态数组用于存放有效的姓名信息。
(3)定义Bundle描写叙述文件MANIFEST.MF。Bundle应用example的MANIFEST.MF文件例如以下:
MANIFEST.MF文件信息 Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Example Bundle Bundle-SymbolicName: example Bundle-Version: 1.0.0 Bundle-Activator: example.osgi.Activator Bundle-Localization: plugin Import-Package: org.osgi.framework;version="1.3.0" Export-Package: example.service |
当中。Bundle-Activator属性指明了实现BundleActivator接口的类,该类用来启动和停止Bundle应用。
Export-Package属性指定了该Bundle输出的共享包,该属性能够使其它的Bundle应用引用我们所定义的服务接口。
(4)创建项目名为exampleClient的Bundle应用,该应用在OSGi平台上查寻并引用example Bundle应用已经注冊的姓名查询服务,然后从标准输入读入用户所输入的姓名信息。推断所输入姓名是否有效。
exampleClient应用的部分源码例如以下。读者可从參考资料中获得完整源码。
ExampleClient Bundle部分源码 public void start(BundleContext context) throws Exception { ServiceReference[] refs = context.getServiceReferences( NameService.class.getName(), "(ClassRoom=*)"); if (refs != null) { try { System.out.println("Enter a blank line to exit."); BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); String name = ""; // Loop endlessly. while (true) { // Ask the user to enter a name. System.out.print("Enter a Name: "); name = in.readLine(); // If the user entered a blank line, then // exit the loop. if (name.length() == 0) { break; } // First, get a name service and then check // if the name is correct. NameService nameservice = (NameService) context .getService(refs[0]); if (nameservice.checkName(name)) { System.out.println("The Name is Correct."); } else { System.out.println("The Name is Incorrect."); } // Unget the name service. context.ungetService(refs[0]); } } catch (IOException ex) { } } else { System.out.println("Couldn't find any name service..."); } } |
Bundle的部署及执行
在Eclipse平台中,选择File-->Export...菜单,将开发的example和exampleClient两个Bundle应用导出成Jar文件,以便将它们部署到OSGi服务平台中。选择将要执行的Bundle应用,鼠标右键点击。在弹出菜单中。选择Run AS-->Equinox FrameWork来启动OSGi服务平台。
在Equinox启动配置控制台中。能够为Bundle应用设置默认的Start Level和Bundle应用是否须要自己主动启动等选项。在本例中,为了解说怎样安装及启动Bundle应用,仅仅将example Bundle应用设为自己主动启动。而exampleClient Bundle应用须要我们用命令安装及启动。
当OSGi Equinox FrameWork启动后,在OSGi控制命令台中输入ss命令,能够查看OSGi服务平台中已经安装的Bundle应用信息及其状态。
如图4所看到的,能够看到当前OSGi服务平台中有两个Bundle处于Active状态,当中。system.bundle_3.2.0.v20060328为OSGi框架的系统Bundle,而example_1.0.0为注冊姓名查询服务的Bundle应用。1.0.0为Bundle应用的版本号号。
图示4 Bundle信息查询在OSGi控制命令台中利用install命令安装exampleClient Bundle应用,用ss命令查看安装后的Bundle应用信息及其状态。
如图5所看到的:
图示5 安装Bundle在OSGi控制命令台中利用start命令安装exampleClient Bundle应用,用户可输入姓名,利用姓名查询服务来推断所输入姓名是否有效。用ss命令查看启动后的Bundle应用信息及其状态。如图6所看到的:
图示6 启动Bundle用户在在OSGi控制命令台中,可利用stop命令来停止指定的Bundle应用。close命令用来停止并退出OSGi控制命令台。关于OSGi Equinox FrameWork控制台命令的具体信息。可查看參考资料。
总结
OSGi服务框架提供了开放的、面向服务的、易于部署的编程模型,在构件面向服务为中心的企业应用的过程中。OSGi 技术正发挥越来越关键的数据。眼下。Eclipse 3.2 体系架构是參照OSGi实现的,Equinox框架是 Eclipse 组织基于OSGi Release 4 的一个实现框架。它实现了OSGi 规范的核心框架和很多标准框架服务。
在本文中。我们利用Eclipse 平台开发了两个Bundle应用,而且在Bundle应用中,声明、实现、注冊并引用了一个简单的服务,最后,将Bundle应用部署到Equinox OSGi服务框架中。
通过本文。读者能够了解怎样开发和部署基于OSGi规范的Bundle应用。