本来上学期就打算研究下OSGI了,博客里也早早地建好了OSGI的分类,可还是因为总总原因拖到了现在。现在上手它,是因为我正在为折腾了一学期的tr-069协议实现demo搞个创新点,于是就想到把这个demo做成OSGI吧。
这次记录的是一个实现OSGI里的Declarative Services的一个“类Helloworld”程序。
首先介绍一下Declarative Services:
Declarative Services 是一个面向服务的组件模型,它制订的目的是更方便地在 OSGi 服务平台上发布、查找、绑定服务,对服务进行动态管理,如监控服务状态以及解决服务之间的复杂的依赖关系等问题。Declarative Services 采用服务组件的延迟加载以及组件生命周期管理的方式来控制对于内存的占用以及启动的快速,很好的解决了传统的 OSGi 服务模型在开发和部署比较复杂应用时内存占用大、启动慢等问题,并且对服务组件的描述采用XML来实现,十分便于用户理解和使用。在 Declarative Services 中,Component 可以是 Service 的提供者和引用者,一个 Component 可以提供 0 至多个 Service,也可以引用 0 至多个 Service,并且采用component 方式封装 Service,方便了对 Service 的复用,从开发者的角度来看,该服务组件模型简化了在 OSGi 服务平台中的编程模型。Declarative Services 规范参考了"Automating Service Dependency Management in a Service-Oriented Component Model"一文的有关概念。而对于component更详细的概念,可以google一下,别人都介绍得很全面了。
下面就切入我的这个小程序吧。
首先在eclipse(我的是3.4的JEE版,安装了在eclipse官方下的equinox插件)里新建一个Plug-in工程,就取名为exporter吧。finish后会自动打开工程的manifest.mf文件,先不管它。新建一个接口:cn.zxy.dstest.Printer,就定义一个public void Print()方法:
package cn.zxy.dstest; public interface Printer { public void print(); }
再新建一个类:cn.zxy.ds.MyPrinter,实现刚才定义的接口:
package cn.zxy.ds; import cn.zxy.dstest.Printer; public class MyPrinter implements Printer { @Override public void print() { System.out.println("OK, congratulations!"); } }
现在在工程的根目录下新建一个文件夹:OSGI-INF,在文件夹里新建一个XML文件:myprinter.xml
<?xml version="1.0" encoding="UTF-8"?> <component name="myprinter"> <implementation class="cn.zxy.ds.MyPrinter" /> <service> <provide interface="cn.zxy.dstest.Printer" /> </service> </component>
component的name属性值可以随便取;service元素表示我这个组件提供的服务,即我实现了一个接口的功能;其它部分应该不难理解。
接下来就要修改刚才最先看到的mf文件了。切换到mf文件编辑环境下面的Runtime标签,在Exported Packages部分Add包含了我们的接口的包:cn.zxy.dstest;切换到Build标签Binary Build树中勾选我们自己建的OSGI-INF文件夹;再切换到MANIFEST.MF标签下,在最后一行添加Service-Component: myprinter.xml,注意文件最后要保证有一个空行,否则会报错。最后保存刚才的修改。现在我们提供服务的bundle就算完成了,我们可以在mf文件编辑器下选择Overview标签,点击launch the framwork来测试我们的bundle。在"osgi>"后输入services,看是否包含了我们编写的服务,信息类似:
{cn.zxy.dstest.Printer}={component.name=myprinter, component.id=0, service.id=26} Registered by bundle: initial@reference:file:../../../home/smartzxy/OSGiTest1/exporter/ [22] No bundles using service.
接下来就要开始编写服务的调用程序了。于上面类似的步骤新建一个工程importer,只是这次不再建接口了,仅仅新建一个类:cn.zxy.test
package cn.zxy.test; import cn.zxy.dstest.Printer; public class MyDS { public void setPrinter(Printer p) { this.printer = p; printer.print(); } public void unsetPrinter(Printer p) { if (this.printer == null) this.printer = null; else System.out.println("Unbond Printer!"); } private Printer printer; }
同样建一个文件夹,建一个xml文件,这次取名为myds.xml:
<?xml version="1.0" encoding="UTF-8"?> <component name="myds"> <implementation class="cn.zxy.test.MyDS"/> <reference name="myprinter" interface="cn.zxy.dstest.Printer" bind="setPrinter" unbind="unsetPrinter" cardinality="0..1" policy="dynamic"/> </component>
和刚才一样相应修改一下mf文件,这次需要Imported Packages里Add我们刚才在上一个工程里导出的包:cn.zxy.dstest,不需要导出包了。相应添加语句并保存:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Importer Plug-in Bundle-SymbolicName: importer Bundle-Version: 1.0.0 Bundle-Vendor: [email protected] Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Import-Package: cn.zxy.dstest Require-Bundle: org.eclipse.osgi;bundle-version="3.4.2", org.eclipse.osgi.services;bundle-version="3.1.200" Service-Component: OSGI-INF/myds.xml
最后就是调试了。此时如果exporter工程没有关闭的话,点击importer工程的mf文件里的launch...链接,会看到
osgi> OK, congratulations!
这说明fw调用成功了。如果停止exporter模块,会看到
osgi> stop 22 Unbond Printer!
(22为exporter模块的编号,在osgi>后输入ss会看到)
如果没有出现上述包括OK...在内的信息,说明新建的importer还不是active状态,则在osgi>后输入start 相应的编号,启动它。
至此,我们的程序算是开发完成了!