火龙果软件-OSGi基础_第1页
火龙果软件-OSGi基础_第2页
火龙果软件-OSGi基础_第3页
火龙果软件-OSGi基础_第4页
火龙果软件-OSGi基础_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 火龙果整理 1.OSGi是什么:Java语言的动态模块系统本文介绍了OSGi是什么,以及OSGi容器的一些现状。OSGi亦称做Java语言的动态模块系统,它为模块化应用的开发定义了一个基础架构。OSGi是什么OSGi亦称做Java语言的动态模块系统,它为模块化应用的开发定义了一个基础架构。OSGi容器已有多家开源实现,比如Knoflerfish、Equinox和Apache的Felix。您可以通过这些容器,把您的应用程序劈分为多个模块单元,这样,您就可以更容易地管理这些模块单元之间的交叉依赖关系。OSGi规范和Servlet规范及EJB规范类似,该规范定义了两种对象,一是容器对外提供的服务对

2、象,另一个是容器和您的应用程序之间必须遵守的契约,其中,服务对象是容器要实现的。您如果想要在OSGi平台上进行开发,首先,您必须要使用OSGi API来创建您的应用,然后将之部署到OSGi容器中。从开发者的角度看,OSGi具有以下优点:a) 您可以在不重启容器的情况下,动态地安装、卸载、启动和停止您的应用程序中的不同模块;b) 对于您应用程序中的某一特定模块,容器可以同时运行该模块的多个版本;c) OSGi为开发嵌入式应用、移动应用、富互联网应用(RIA)提供了非常优秀的基础架构如果说您使用Servlet容器开发您的网络应用,使用EJB容器开发交易式应用,您可能会问,为什么我们还需要另外的容器

3、呢?对这个问题的简短回答是,OSIG容器是专门为开发复杂的Java应用准备的,在这些应用的开发过程中,您非常需要将这些应用分割为一个个的模块。在本系列以后的文章中,我将针对这个问题进行展开并深入回答。1. OSGi在企业开发中的应用OSGi联盟(OSGiAlliance)于1999年3月开始着手制定OSGi规范,其主要目的就是要制定一套开放式标准,以便向局域网及其中的设备提供可管理的服务;其基本思路是,一旦您在网络设备(如服务器和嵌入式设备)上使用了OSGi服务平台,您就可以在网络上的任何地方管理这些设备上运行的软件组件的生命周期,可以在后台对这些组件进行安装、升级或卸载,但不需要打断该设备的

4、正常运行。近年来,OSGi技术在嵌入式系统及网络设备市场得到广泛应用。现在,由于Eclipse的成功,OSGi在企业开发中逐渐成为切实可行的、较有价值的一种技术。1.1. 业界对OSGi的支持逐渐上升2003年,Eclipse开发团队开始想办法提高Eclipse工具集的模块化,以便让它成为更加动态的富客户端平台。Eclipse团队最终选中OSGi框架作为其组件的运行时模型,2004年6月发布的Eclipse3.0就是第一个基于OSGi平台的版本。现在几乎所有的企业应用服务器都支持OSGi,Spring也通过一个叫“OSGi服务平台上的Spring动态模型(亦称之为OSGiSpring)”的项目

5、来支持OSGi。该项目提供OSGi基础架构,以便我们在Spring的企业开发中更容易使用OSGi。2. 开放源码的OSGi容器从企业开发者的角度看,OSGi容器的要求很低,您可以很容易地把它嵌入到企业应用中,比如我们在开发Web应用时,我们可以把这个Web应用分为多个模块,一个模块负责视图层,另一个模块负责DAO层,第三个模块负责数据访问层,如果我们使用OSGi容器来管理这些模块之间的交叉依赖,我们就可以在不用重启该Web应用的前提下,将DAO层从速度较慢的升级到速度较快的DAO。只要您的应用和OSGi规范兼容,您的应用就应该可以运行在任何OSGi容器中,现在比较流行的开放源码的OSGi容器有

6、以下三种:a) Equinox容器是参照OSGi规范第4版实现的,它构成了Eclipse IDE的核心模块化的Java运行时;它实现了OSGi规范4中规定的必须强制实现的功能,同时,它也实现了OSGi规范中大部分的可选功能;b) Knoflerfish是OSGi规范第3版和第4版的开源实现,它实现了OSGi规范规定的必须实现的功能及部分可选功能;c) Apache的Felix是Apache软件基金会实现的OSGi开源容器,至本文截稿时为止,该容器还没有和OSGi规范完全兼容。在本文中,我们将使用Equinox作为我们的OSGi容器。对OSGi是什么的介绍就先到这里,下面的部分将继续介绍OSGi

7、的基础知识。2.OSGi Bundle之Hello World本文介绍如何开发OSGi Bundle,使用Hello World做为范例。开发工具为Eclipse,使用容器为Equinox。开发一个简单的Hello World的OSGi Bundle(OSGi绑定包)在OSGi中,软件是以Bundle的形式发布的。一个Bundle由Java类和其它资源构成,它可为其它的Bundle提供服务,也可以导入其它Bundle中的Java包;同时,OSGi的Bundle也可以为其所在的设备提供一些功能。Eclipse为开发OSGiBundle提供了优秀的支持,它不仅提供了向导来创建OSGi Bundle

8、,而且还提供了内嵌的Equinox容器,您可以使用该容器执行和调试OSGi插件。请注意每一个Eclipse插件,从本质上说,都是一个OSGi Bundle,只是这个OSGiBundle多加了一些Eclipse专用的代码而已。下面我们来看看如何使用Eclipse开发一个简单的OSGi的HelloWorld Bundle。3.1.新建Bundle1) 在Eclipse中,点击“File->New->Project”菜单,您将会看到新项目创建对话框;2) 在新项目对话框中,选择“Plug-inProject(插件项目)”并点击“Next(下一步)”按钮,您将看到插件项目对话框;3) 在插

9、件项目对话框中,请键入下列值:Project Name(项目名称):com.javaworld.sample.HelloWorldTarget Platform(目标平台):an OSGiFramework->Standard (OSGi框架->标准)4) 对其它的要求输入值采用缺省值,并点击“Next(下一步)”按钮,您将会看到插件上下文对话框;5) 在插件上下文对话框中,请选择缺省值并点击“Next(下一步)”按钮;6) 在模板对话框中,请选择“Hello OSGiBundle(你好,OSGi包)”模板,然后点击“Finish(完成)”按钮完成该项目。Eclipse将花几秒钟生

10、成HelloWorld Bundle模板代码,它将新建两个文件:Activator.java和MANIFEST.MF,下面,让我们看看这两个文件:3.1.1. Activator.java文件源代码清单1.Activator.java1. package com.javaworld.sample.helloworld;  2. importorg.osgi.framework.BundleActivator;  3. importorg.osgi.framework.BundleContext;  4. publicclass Activato

11、r implements BundleActivator   5. public void start(BundleContext context)throws Exception   6. System.out.println("Helloworld");  7.  8. public void stop(BundleContext context)throws Exception   9. S

12、ystem.out.println("GoodbyeWorld");  10.  11.  12.  如果您想让您开发的Bundle能在其启动或关闭时通知自身,那么您应新建一个类,让它实现BundleActivator接口,同时,您还需要遵行下列规则:这个实现了BundleActivator接口的类必须有一个public的、不带参数的构造函数,这样,OSGi框架就能调用该类的Class.newInstance()方法创建这个BundleActivator对象;容器将调用Activator类的start()方法来启动Bundle,因此,

13、我们可以在start()方法中执行一些资源初始化的操作,例如,我们可以在该方法中获取数据库连接,以备后用。这个start()方法的唯一参数是一个BundleObject对象,Bundles可以通过该对象和OSGi框架通讯,我们可以从该对象中获取OSGi容器相关的一些信息;如果某个Bundle抛出异常,容器将之置为“stopped(已停止)”状态,此时,这个Bundle就不能对外提供服务。如果我们要关闭一个Bundle,容器将调用Activator类中的stop()方法。因此,我们可在stop()方法中执行一些资源清理任务,比如释放数据库连接。一旦Activator类准备就绪,您就可以通过MAN

14、IFEST.MF文件把该包的合法名称传给容器。下面,我们就看看这个MANIFEST.MF文件。3.1.2. MANIFEST.MF文件该文件是Bundle的部署描述文件,其格式和正常JAR文件包中的MANIFEST.MF文件相同,因此它由一系列的属性及这些属性对应的值组成,属性名位于每一行的开头,我们可以称其为属性头。OSGi规范规定,您可以使用属性头向容器描述您的Bundle。您的HelloWorld Bundle的MANIFEST.MF文件看起来应该如清单2所示:源代码清单2. Hello World Bundle中的MANIFEST.MF文件1. Manifest-Version:1.0

15、 2. Bundle-ManifestVersion:2 3. Bundle-Name:HelloWorld Plug-in  4. Bundle-SymbolicName:com.javaworld.sample.HelloWorld  5. Bundle-Version:1.0.0 6. Bundle-Activator:com.javaworld.sample.helloworld.Activator  7. Bundle-Vendor:JAVAWORLD  8. Bundle-Localization:pl

16、ugin  9. Import-Package:org.osgi.framework;version="1.3.0" 10.  我们来看看这个文件中使用的属性头:Bundle-ManifestVersion该属性头告诉OSGi容器,本Bundle将遵循OSGi规范,数值2表示本Bundle和OSGi规范第4版本兼容;如果该属性的数值为1,那么则表示本包和OSGi版本3或更早版本兼容。Bundle-Name该属性头为本Bundle定义了一个简短的、可以阅读的名称;Bundle-SymbolicName这个属性头为本Bundle定义了一个唯一的、非

17、本地化的名字;当您需要从别的Bundles中访问某一指定的Bundle时,您就要使用这个名字。Bundle-Version该属性头给出了本Bundle的版本号。Bundle-Activator该属性头给出了本Bundle中使用的监听器类名字,这个属性值是可选的。监听器将对Activator中的start()和stop()方法监听。在程序清单2中,该属性头的值为com.javaworld.sample.helloworld.Activator。Bundle-Vendor该属性头是对本Bundle发行商的表述。Bundle-Localization该属性头包含了本Bundle的本地化文件所在的位置

18、,我们的HelloWorld Bundle中并没有本地化文件,但Eclipse IDE仍自动产生这个属性头Import-Package该属性头定义了本Bundle中引入的Java包,我将在本文后面的依赖性管理小节中详细讲解这个问题。现在,HelloWorld Bundle已经准备就绪,让我们来运行并看看它的输出结果。3.2. 运行Bundle我在前面提到,Eclipse IDE中有一个内嵌的EquinoxOSGi容器,您可以利用它来执行或调试OSGi Bundle。请按照下面步骤执行刚才的HelloWorld Bundle:1 ) 单击RunàRun 菜单(译者注,在Eclipse3

19、.3中,请单击RunàOpen Run Diglog菜单);2) Eclipse会打开“Create,manage and run configuration(新建、管理和运行配置)”对话框,请双击”EquinoxOSGi Framework”按钮,Eclipse将打开运行时配置对话框;3) 在上面的对话框中,将Name(名称)输入框的值改为HelloWorld Bundle;4) 您会注意到在Workspace插件目录下,有一个名为com.javaworld.sample.HelloWorld的插件,请选中它;在TargetPlatform(目标平台)下,请确保org.eclips

20、e.osgi插件被选中。您的Run(运行)对话框应该看起来如图1所示: 图1. HelloWorld Bundle的运行配置5) 现在,请单击Run(运行)按钮,您应该看到控制台视图上打印出“HelloWorld”。其实,Eclipse是在控制台视图中打开OSGi控制台。3.2.1. OSGi控制台OSGi控制台是OSGi容器的命令行界面,您可以在这个控制台上启动、停止、安装、更新和删除Bundles。在EclipseIDE中,请点击该控制台视图获得焦点,然后按回车键,这时您可以看到OSGi提示符,如图2所示:(译者注,在Eclipse3.3中,如果您没有看到OSGi提示符,请在图1

21、的运行配置中,点击Arguments标签,然后在ProgramArguments(程序参数)输入框中键入“-console”,然后再次运行该Bundle)。 图2. OSGi控制台和HelloWorldActivator.java下面是几个经常使用的OSGi命令,您可以使用这些命令与OSGi容器进行交互。1. ss: 该命令显示所有已安装的Bundles及它们的状态,它将显示Bundle ID,Bundle的简短名称及Bundle状态;  2. start< bundleid>: 该命令将启动一个Bundle;  

22、;3. stop< bundleid>: 该命令将停止一个Bundle;  4. update< bundleid>: 该命令使用新的JAR文件更新一个Bundle;  5. install< bundleid>: 该命令将一个新的Bundle安装到OSGi容器;  6. uninstall< bundleid>: 从OSGi容器中卸载一个已安装的Bundle。   请注意,这些命令是OSGi规范中规定的,因此,您可以

23、使用它们和任何OSGi容器交互。 读到这里,希望您对OSGi Bundle的开发有了一个大致的了解。3.OSGi依赖性管理:Bundle访问域OSGi允许您把您的应用程序分成多个模块,并能管理这些模块之间的依赖性。本文介绍了OSGi依赖性管理的概念。OSGi依赖性管理 OSGi允许您把您的应用程序分成多个模块,并能管理这些模块之间的依赖性。为了达到这个目的,它引入了Bundle访问域的概念。Bundle中类的缺省访问范围只对本Bundle内部可见,但对其它任何Bundle都是不可见的;在Bundle内部,类的可访问性遵循Java语言的一般规范。那么,您如果想要从一个Bundle中访问另一个Bu

24、ndle中的类,您应该怎么办呢?解决方法是将源Bundle中的包导出来,然后把它们导入到目标Bundle中。在本小结中,我们将通过一个示例程序说明这个概念。首先,我们新建一个名com.javaworld.sample.HelloService的Bundle,并从其中导出一个包,然后将该包导入到我们的com.javaworld.sample.HelloWorld Bundle中。4.1. 导出Java包我们开始新建一个com.javaworld.sample.HelloServiceBundle,并从其中导出一个Java包,具体步骤如下:1) 新建com.javaworld.sample.Hel

25、loService Bundle,具体步骤请参见上小节中新建com.javaworld.sample.HelloWorldBundle的步骤;2) 在HelloService Bundle中,新建一个com.javaworld.sample.service.HelloService.java接口,其源代码如清单3所示。源代码清单3. HelloService.java1. package com.javaworld.sample.service;  2. public interface HelloService   3. publi

26、c String sayHello();  4.  5.  3) 新建类com.javaworld.sample.service.impl.HelloServiceImpl.java,该类实现HelloService接口,其源代码如清单4所示。源代码清单4. HelloServiceImpl.java1. package com.javaworld.sample.service.impl;  2. import com.javaworld.sample.service.HelloService;  3.

27、public class HelloServiceImpl implements HelloService   4. public StringsayHello()   5. System.out.println("InsideHelloServiceImple.sayHello()");  6. return"Say Hello"  7.  8.  9.  4) 请在您的Eclipse Manifest编

28、辑器中打开HelloService包中的MANIFEST.MF文件,点击“Runtime(运行时)” 标签,在“导出包”小节,单击“Add(添加)”按钮,并选择com.javaworld.sample.service包。这时,HelloServiceBundle中的MANIFEST.MF文件代码应如源代码清单5所示。源代码清单5. HelloService Bundle中的Manifest文件1. Manifest-Version: 1.0 2. Bundle-ManifestVersion: 2 3. Bundle-Name: HelloSe

29、rvice Plug-in  4. Bundle-SymbolicName:com.javaworld.sample.HelloService  5. Bundle-Version: 1.0.0 6. Bundle-Vendor: JAVAWORLD  7. Bundle-Localization: plugin  8. Export-Package: com.javaworld.sample.service  9. Import-Package:org.osgi.framework;

30、version="1.3.0" 10.  您可以看到,HelloService Bundle中的MANIFEST.MF文件和HelloWorldBundle非常相似,唯一的区别就是多了一个Export-Package属性头,该属性头的值为com.javaworld.sample.service;Export-Package属性头通知OSGi容器,其它Bundle可以从HelloService Bundle外面访问com.javaworld.sample.service包中的类。请注意,在示例代码中,我们只暴露了接口类HelloService,而没有暴露其

31、实现类的HelloServiceImpl。4.2. 导入Java包下面,我们将从HelloServiceBundle中导出的com.javaworld.sample.service包并将其导入到HelloWorldBundle中,具体步骤如下:1). 请在com.javaworld.sample.HelloWorld Bundle中找到MANIFEST.MF文件,并在Manifest编辑器中打开,点击“Dependencies(依赖性)”标签,然后点击“ImportPackage(导入包)”按钮,将com.javaworld.sample.service添加为导入包,这时,您的HelloWor

32、ldBundle中的MANIFEST.MF文件内容应如源代码清单6所示:源代码清单6. HelloWorld Bundle中的MANIFEST.MF文件1. Manifest-Version: 1.0 2. Bundle-ManifestVersion: 2 3. Bundle-Name: HelloWorld Plug-in  4. Bundle-SymbolicName: com.javaworld.sample.HelloWorld  5. Bundle-Version: 1.0.0

33、60;6. Bundle-Activator: com.javaworld.sample.helloworld.Activator  7. Bundle-Vendor: JAVAWORLD  8. Bundle-Localization: plugin  9. Import-Package: com.javaworld.sample.service,  10. org.osgi.framework;version="1.3.0" 11.  从上面的代码可以看出,Import-

34、Package属性头的值是一个由逗号分隔的字符串,这是您想导入包的列表。在HelloWorldBundle示例代码中,我们引入了两个包,即com.javaworld.sample.service和org.osgi.framework。org.osgi.framework包中包含有OSGi框架类,比如,在HelloWorldBundle中的Activator.java中用到的BundleContext和BundleActivator类都属于这个包。2) 下面,请在Eclipse Java编辑器中打开com.javaworld.sample.helloworld.Activator.java,您会

35、注意到,您现在可以访问HelloService接口,但不能访问HelloServiceImpl实现类,这是因为HelloServiceBunlde只导出了com.javaworld.sampel.service包,同时HelloWorldBundle也导入了这个包。HelloServiceImpl是HelloServiceBundle的一个内部类,任何其它的Bundle都不能访问它。4.3. 类级别上的访问域如果您运行示例的HelloService服务包,它会在Eclipse控制台上打印出”HelloWorld”。但是,如果您想在HelloWorld Bundle的Activator中访问He

36、lloServiceImpl类,这时,编译没有问题,但在OSGi容器中运行这个Bundle时会抛出异常。OSGi容器是如何能将jar文件中的一些类隐藏掉,而让另外一些类可见呢?这是因为OSGi容器使用Java类加载器来管理类的可见性,OSGi容器为每个Bundle创建不同的类加载器,因此每个Bundle能访问位于下列位置中的类:a) 位于Java启动类路径下的、所有以Java.*开头的包中的类;b) 位于OSGi框架类路径下的类,通常有一个独立的类加载器负责加载框架的实现类及关键的接口类;c) 位于Bundle空间中的类,这些类通常包含在与Bundle相关的jar文件中,以及加到这个Bundl

37、e中的其它jar包中的类。d) 导入包中的类,例如,HelloWorld Bundle导入了com.javaworld.sample.service包,因此它能访问该包中的类。Bundle级别的访问域是OSGi一个非常强大的功能,例如,它可以让您安全地更新HelloServiceImpl.java类,而不必担心依赖于这个类的代码受到破坏。以上就大概介绍了OSGi依赖性管理的概念。4.OSGi服务:非常适合SOA的架构本文介绍OSGi服务。OSGi架构非常适合我们实现面向服务的应用(SOA)。OSGi具有隐藏真实的服务实现类的能力,所以它为面向服务的应用提供了良好的类与接口的组合。OSGi服务前

38、面我们提到,OSGi架构非常适合我们实现面向服务的应用(SOA)。它可以让Bundles导出服务,而其它的Bundles可以在不必了解源Bundles任何信息的情况下消费这些导出的服务。由于OSGi具有隐藏真实的服务实现类的能力,所以它为面向服务的应用提供了良好的类与接口的组合。在OSGi框架中,源Bundle在OSGi容器中注册POJO对象,该对象不必实现任何接口,也不用继承任何超类,但它可以注册在一个或多个接口下,并对外提供服务。目标Bundle可以向OSGi容器请求注册在某一接口下的服务,一旦它发现该服务,目标Bundle就会将该服务绑定到这个接口,并能调用该接口中的方法。下面我们举个例

39、子,以便我们能更好理解与OSGi相关的这些概念。5.1. 导出服务在本小节中,我们将更新HelloService Bundle,以便它能把HelloServiceImpl类的对象导出为服务,具体步骤如下:1) 修改com.javaworld.sample.HelloService Bundle中的MANIFEST.MF文件,让它导入org.osgi.framework包(译者注,这一步我们已经完成);2) 新建Java类com.javaworld.sample.impl.HelloServiceActivator.java,其源代码如清单7所示;源代码清单7. HelloServiceActi

40、vator.java1. public class HelloServiceActivator implements BundleActivator   2. ServiceRegistrationhelloServiceRegistration;  3. public void start(BundleContext context)throws Exception   4. HelloService helloService = n

41、ewHelloServiceImpl();  5. helloServiceRegistration=context.registerService(HelloService.class.getName(), helloService, null);  6.  7. public void stop(BundleContext context)throws Exception   8. helloServiceRegistration.unregister();  9. &#

42、160;10.  11.  请注意,在源Bundle中,我们应使用BundleContext.registerService()方法导出服务,这个方法带三个参数:a) 该方法第一个参数为您要注册的服务的接口名称。如果您想把您的服务注册到多个接口下,您需要新建一个String数组存放这些接口名,然后把这个数组作为第一个参数传给registerService()方法。在示例代码中,我们想把我们的服务导出到HelloServer接口名下;b) 第二个参数是您要注册的服务的实际Java对象。在示例代码中,我们导出HelloServiceImpl类的对象,并将其作为服务;c) 第三个

43、参数为服务的属性,它是一个Dictionary对象。如果多个Bundle导出服务的接口名相同,目标Bundle就可以使用这些属性对源Bundle进行过滤,找到它感兴趣的服务。3) 最后,请修改HelloServiceBundle中的MANIFEST.MF文件,将Bundle-Activator属性头的值改为com.javaworld.sample.service.impl.HelloServiceActivator。现在HelloService Bundle就可以导出HelloServiceImpl对象了。当OSGi容器启动HelloServiceBundle时,它会将控制权交给HelloSe

44、rviceActivator.java类,HelloServiceActivator将HelloServiceImpl对象注册为服务。下面,我们开始创建该服务的消费者。5.2. 导入服务在本小节中,我们将修改上面开发的HelloWorld Bundle,以便让它成为HelloService服务的消费者。您主要需要修改HelloWorldBundle中的Activator.java代码,修改后的代码如源代码清单8所示:源代码清单8. HelloWorld Bundle中的Activator.java1. packagecom.javaworld.sample.helloworld;  

45、2.  3. importorg.osgi.framework.BundleActivator;  4. importorg.osgi.framework.BundleContext;  5. importorg.osgi.framework.ServiceReference;  6. importcom.javaworld.sample.service.HelloService;  7.  8. publicclass Activator implements BundleActivator 

46、  9. ServiceReference helloServiceReference;  10. public void start(BundleContext context)throws Exception   11. System.out.println("HelloWorld!");  12. helloServiceReference=context.getServiceReference(HelloService.class.getName();  1

47、3. HelloService helloService=(HelloService)context.getService(helloServiceReference);  14. System.out.println(helloService.sayHello();  15.  16.  17. public void stop(BundleContext context)throws Exception   18. System.out.println("Goodbye&

48、#160;World!");  19. context.ungetService(helloServiceReference);  20.  21.  22.  在上面的代码中,BundleContext.getServiceReference()方法将为注册在HelloService接口下的服务返回一个ServiceReference对象。如果存在多个HelloService服务,该方法会返回排行最高的服务(服务的排行是通过Constants.SERVICE_RANKING属性指定的)。您一旦获得ServiceReference对象

49、,您就可以调用其BundleContext.getService()方法获取真实的服务对象。您可以参照运行Bundle的方法运行上面的示例应用,请点击“RunàRun”菜单,并确保HelloWorld和HelloService这两个Bundle被选中。当您启动HelloServiceBundle时,您会在控制台上看到“InsideHelloServiceImple.sayHello()”,这个消息是由HelloServiceImpl.sayHello()方法打印出来的。5.3. 创建服务工厂在上节中,我们学会了如何使用OSGi框架新建一个Java对象,并把它注册为一个服务,然后让其它

50、的Bundle去消费这个服务。如果您看一下HelloServiceActivator.start()方法,您会注意到我们在start()方法中新建了HelloServiceImpl类对象,然后将它注册到HelloService接口名下。这样注册后,任何其它的Bundle在请求HelloService服务时,OSGi容器将返回同一对象。在大多数情况下,这样的实现方法没有问题。但是,比如说我们要为每一个Bundle消费者返回不同的HelloServiceImpl对象,再比如说,您的服务对象要提供的服务为打开一个数据库连接,但并不是马上就打开它,而是在真正需要的时候才打开这个数据库连接。对这两种情况

51、,我们的解决方法是,新建一个类实现ServiceFactory接口,并把该类的对象注册为服务,但并不是注册实际的服务对象。一旦您完成这一步,其它Bundle在请求该服务时,您的ServiceFactory实现类将接管该请求,ServiceFactory会为每个Bundle新建一个服务对象,并将真实服务的创建时间延迟到有人真正需要该服务的时候。下面我们将使用ServiceFactory更新我们上面开发的com.javaworld.sample.HelloServiceBundle,具体步骤如下:1) 新建工厂 类HelloServiceFactory.java,源代码如清单9所示。源代码清单9

52、. HelloServiceFactory.java1. public class HelloServiceFactory implements ServiceFactory  2. private int usageCounter = 0;  3. public Object getService(Bundle bundle,ServiceRegistration registration)   4. System.out.print

53、ln("Create objectof HelloService for " + bundle.getSymbolicName();  5. usageCounter+;  6. System.out.println("Number ofbundles using service " + usageCounter);  7. HelloService helloService =

54、0;newHelloServiceImpl();  8. return helloService;  9.  10. public void ungetService(Bundle bundle,ServiceRegistration registration, Object service)   11. System.out.println("Release objectof HelloService for "&

55、#160;+ bundle.getSymbolicName();  12. usageCounter-;  13. System.out.println("Number ofbundles using service " + usageCounter);  14.  15.  16.  从上面的代码中,我们可以看到,ServiceFactory接口定义了两个方法:a) getService()方法:当某个Bundle第一次使用BundleCont

56、ext.getService(ServiceReference)方法请求一个服务对象时,OSGi框架会调用该方法。在源代码清单9中,我们用这个方法为每个Bundle新建并返回不同的HelloServiceImpl对象,如果这个对象不是null,OSGi框架会缓存这个对象。如果同一个Bundle再次调用BundleContext.getService(ServiceReference)方法,OSGi将返回同一个服务对象。b) ungetService()方法:当Bundle释放服务时,OSGi容器可以调用该方法销毁服务对象。在源代码清单9中,我们使用usageCounter变量来跟踪服务的使用数

57、目,并打印出该服务的客户端数量。2) 修改HelloService Bundle中的HelloServiceActivator.java的start()方法,让它注册到ServiceFactory接口名下,而不是注册到HelloService接口。详细代码如清单10所示:源代码清单10. 修改后的HelloServiceBundle中的HelloServiceActivator.java1. package com.javaworld.sample.service.impl;  2. importorg.osgi.framework.BundleActivator; 

58、60;3. importorg.osgi.framework.BundleContext;  4. importorg.osgi.framework.ServiceRegistration;  5.  6. importcom.javaworld.sample.helloservice.HelloServiceFactory;  7. importcom.javaworld.sample.service.HelloService;  8.  9. publicclass HelloServiceActivator 

59、implements BundleActivator   10. ServiceRegistrationhelloServiceRegistration;  11. public void start(BundleContext context)throws Exception   12. HelloServiceFactory helloServiceFactory= new HelloServiceFactory();  13. helloServic

60、eRegistration=context.registerService(HelloService.class.getName(), helloServiceFactory,null);  14.  15. public void stop(BundleContext context)throws Exception   16. helloServiceRegistration.unregister();  17.  18.  19.  现在,您可以试运行示例代码

61、。您会注意到,当HelloWorld Bundle启动时,服务计数器变为1;当HelloWorldBundle停止时,服务计数器的数目将变为0。5.4. 跟踪服务在“OSGi服务”小节,您学会了如何使用服务的接口名搜索服务。但如果有多个Bundle使用同一接口名注册服务,那会发生什么呢?这时,OSGi容器将返回排行最高的服务,即,返回注册时那个SERVICE_RANKING属性值最大的服务。如果有多个服务的排行值相等,那么OSGi容器将返回PID值最小的那个服务。但是,如果您的服务消费者需要了解某一接口下的服务对象何时注册、何时取消注册,这时,您应使用ServiceTracker类。下面,我们

62、看看如何使用服务跟踪器来修改我们的示例代码,具体步骤如下。1) 修改HelloWorldBundle的MANIFEST.MF文件,让它导入org.osgi.util.tracker包;2) 新建类HelloServiceTracker.java,其源代码参见清单11。源代码清单11.HelloServiceTracker.java1. public class HelloServiceTracker extends ServiceTracker   2.  3.     public&

63、#160;HelloServiceTracker(BundleContext context)   4.  5.         super(context, HelloService.class.getName(),null);  6.  7.       8.  9.     public Object addingServ

64、ice(ServiceReference reference)   10.  11.         System.out.println("Inside HelloServiceTracker.addingService " + reference.getBundle();  12.  13.         ret

65、urn super.addingService(reference);  14.  15.       16.  17.     public void removedService(ServiceReference reference, Object service)   18.  19.         

66、System.out.println("Inside HelloServiceTracker.removedService " + reference.getBundle();  20.  21.         super.removedService(reference, service);  22.  23.       24.  25.  26.  在上面的HelloSerivceTracker类的构造函数中,您可以看到,我们把HelloService接口名传入其父类中,这相当于说,HelloServiceTracker应跟踪注册到HelloService接口名下的所有服

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论