OpenDoc Series':OSGI实战(六):3

 由  ValRay 发布

6.5. 测试和调试

对于测试的支持无疑是现在对于框架的重要考评点,那么来看看基于 OSGI 框架的系统怎么做测试呢,由于做集成测试的方法只和系统的结构(B/S、C/S)有关,和框架没什么关系,所以在这里只谈基于 OSGI 框架的系统是如何做单元测试的。

按照正确的方法,应该是先写测试,由于需要先介绍下基于 OSGI 框架的开发,为了避免注意力的分散,所以就把测试的部分挪到这里专门讲解了。 编写单元测试时重要的就两点:

  • 设置测试类的依赖(通过 Mock、实例化等方法);
  • 测试在某种输入的情况下方法执行的输出是否和预期的结果一致。

在做单元测试时复杂的部分往往就是设置测试类的依赖,典型的就像 EJB 中的session bean 的测试,由于它需要依赖 EJB Container,所以是比较麻烦的。

之前编写的几个 Service 没有依赖的情况,就只需要测试在某种输入的情况下方法执行的输出和预期的结果是否一致,这就很容易编写了,具体参见附件 classic 目录下的LDAPValidatorImplTest、DBValidatorImplTest 以及 ConfigFileValidatorImplTest。

在用户登录模块这个例子中,复杂的就是登录响应 Servlet 的测试,这个登录响应 Servlet 需要依赖 OSGI 框架的 BundleContext 获取用户登录验证服务,同时还需要依赖 HttpServletRequest 和 HttpServletResponse,在没法实例化类所依赖的环境时,只能采用 Mock 的方法来实现,代码见附件 classic 目录下的 LoginServletTest。

在这种情况下会发现基于OSGI框架的单元测试并不好做,这主要是因为在之上的例子 中对于服务的获取、注册都是采用BundleContext来完成的(也就意味着需要依赖OSGI 框架,HttpServletRequest需要Mock和OSGI框架没什么关系),在后续Declarative Services的章节中介绍采用DI方式来实现时,就不需要Mock BundleContext来获取服务了,到时会将测试这部分的代码进行重写。

调试也是关注的重点,由于可以在 Eclipse 中启动基于 Equinox 开发的系统,那么一切都不是问题,和普通 Java 工程进行调试的方法没有任何区别,设置断点,Debug,就 OK 了,在运行到断点对应的代码时就进入熟悉的调试视图了。

6.6. 发布基于 OSGI 的系统

经过上面的步骤,完成了用户登录验证模块的开发工作,一直我们都是通过 Eclipse 直接来启动这个系统的,但发布给用户的系统不可能让用户通过 Eclipse 来运行系统,那么如何发布一个单独运行的基于 OSGI 的系统呢?

  • 第一步:建立独立的 Equinox 运行环境;

    在 硬 盘 上 建 立 一 个 Demo 目 录 , 从 Eclipse/Plugins 目 录 下 复 制org.eclipse.osgi_3.2.0.v20060510.jar 到此目录下,改名equinox.jar,编写一个 run.bat, 其中内容为:

    java -Dorg.osgi.service.http.port=8080 -jar equinox.jar –console
    

如果不需要指定 http 的端口的话,可以编写成:
java –jar equinox.jar -console

双击 run.bat,如看到 osgi>则表明启动成功了,输入 ss 回车,这个时候会看到列 表中只有一个 system bundle。

  • 第二步:导出各 Bundle 工程为 jar;  

    以为复杂的 UserValidatorWebBundle 为例,首先打开 MANIFEST.MF,在里面 Runtime 标签项的 Classpath 中增加对于 lib 中 jar 包的引用:

打开工程里的 build.properties 文件,选中其中的 lib 目录:

选中 UserValidatorWebBundle 工程,右键,选择 Export,选中弹出页面中的 Deployable plug-ins and fragments:

在进入的窗口中的 Available Plug-ins and Fragments 中已经默认选中了 UserValidatorWebBundle,选中 Destination 标签项中的 Directory,browse,填写或 选择导出的目录,点击 Finish,在导出的目录中可以看到 plugins 目录,其中有 UserValidatorWebBundle_1.0.0.jar 文件,这就是我们所需要的。

按照同样类似的方法导出其他的几个工程,在目前这个用户登录验证模块中会形成五个 jar 文件,也可以一次性的把五个工程全部导出。

  • 第三步:安装各 Bundle 到 Equinox 中。 在用户登录验证模块还用到了 Equinox 提供的其他三个 Bundle:OSGI Services API、ServletAPI、HttpService,在 demo 下建立一个 bundles 目录,将第二步时生成的五个 Bundle 复制到 bundles 目录下,同样再从 Eclipse/Plugins 目录中复制 org.eclipse.equinox.http1.0.0.v20060601a.jar 、 org.eclipse.equinox.servlet.api1.0.0.v20060601.jar 、 org.eclipse.osgi.services_3.1.100.v20060511.jar 到 bundles 目录中。

有两种方法将 Bundle 安装到 Equinox 中:

第一种

运行之前编写的 run.bat,在 osgi>后输入 install reference:file:bundles/ UserValidatorBundle_1.0.0.jar,回车,这样就完成了 UserValidatorBundle 的安 装了,输入同样的命令把 bundles 目录中的其他 Bundle 也安装上,安装完毕 之后,在 osgi>后输入 start 1 回车,之后相聚输入 start 2、start 3、start 4、start 5、start 6、start 7、start 8;这样所以的 Bundle 就启动好了,输入 ss 看看目前安装的 Bundle 的状态:

打开浏览器,输入http://localhost:8080/demo/page/login.htm,看到了之前在 Eclipse启动后同样的页面吧。

之后在 osgi>输入 exit,回车后退出系统,以后需要启动系统时只需要运行 run.bat 就可以了。

经过这些步骤后,就形成了单独运行的基于 OSGI 的系统。

第二种

这种方法比较简单,在 Demo 目录下建立 configuration 目录,在其中放入 config.ini 文件,这个文件示例如下:

osgi.noShutdown=true 
# 当前系统下运行的 Bundle,可以在此指定 Bundle 的启动顺序,在后续的 
# StartLevel Service章节中会详细的介绍 
osgi.bundles=reference\:file\:bundles/ConfigFileValidatorBundle_1.0.0.jar@start,ref erence\:file\:bundles/DBValidatorBundle_1.0.0.jar@start,reference\:file\:bundles/LD APValidatorBundle_1.0.0.jar@start,reference\:file\:bundles/org.eclipse.equinox.http
_1.0.0.v20060601a.jar@start,reference\:file\:bundles/org.eclipse.equinox.servlet.api
_1.0.0.v20060601.jar@start,reference\:file\:bundles/org.eclipse.osgi.services_3.1.10 0.v20060511.jar@start,reference\:file\:bundles/UserValidatorBundle_1.0.0.jar@start, reference\:file\:bundles/UserValidatorWebBundle_1.0.0.jar@start osgi.bundles.defaultStartLevel=4

配置完毕后双击run.bat,就启动这个单独运行的基于OSGI的系统了,在后续 的StartLevel Service章节中会详细的介绍config.ini文件。

当然,可以把上面的这些手动部署的步骤转为采用 ant 或 maven 编写成脚本来实现自动部署,但总体来说,目前发布单独运行的基于 OSGI 的系统还不是非常的方便,也 许在不久的将来可以直接导出在 Eclipse Run…中配置的 OSGI 运行系统。

6.7. Equinox 基于 OSGI 的扩展

Equinox 除了完整实现 OSGI R4 规范以外,还吸取了 Eclipse 3.0 以前版本中那套插件框架中优秀的地方,如 Extension registry 机制、Eclipse Adapter 机制等,其中重要的就是 Extension Registry,Extension Registry 是 Eclipse 插件机制中关键的部分,它为 插件的扩展提供了一种实现的机制,Bundle 通过发布扩展点的方式来定义 Bundle 可扩展的部分,当需要扩展 Bundle 的时候只需要实现 Bundle 提供的扩展点的接口就可以了,通过这样的方式就可以完成 Bundle 的扩展了,在 Eclipse 中有很多地方采取了这种方式来扩展插件,如 Eclipse 中的菜单、视图等,都可以通过实现扩展点来增加新的菜单、新的视图。

网上关于如何使用Eclipse扩展点的介绍非常的多,Equinox的使用方法是基本相同的, 将 org.eclipse.equinox.registry Bundle 放入 Eclipse 的 Plugins 目录即可,其他过程就和 Eclipse 扩展点的使用方法完全相同,在此就不再重复去写了。

Equinox 作为 OSGI R4 的 RI,其对于 OSGI 的扩展一定程度上会对 OSGI R5 产生影响, 但在目前的阶段使用的时候要注意,如果开发要保证符合 OSGI 标准,那么就不要使用 Equinox 对于 OSGI 的扩展,这个在使用 Eclipse 进行开发时可以指定仅使用标准的 OSGI 框架。

6.8. 现有类型系统基于 OSGI 的开发

基于 OSGI 进行系统开发的时候和传统的开发方式有所不同,来看看对于 B/S、C/S 以及嵌入式系统基于 OSGI 怎么去开发。

6.8.1. B/S

例子中的用户登录验证模块是个典型的 B/S 结构的系统模块,采用 HttpService 来作为实现 B/S 结构的系统是基于 OSGI 开发 B/S 结构系统的方法之一,不过 HttpService 对于 B/S 结构的系统来说支持的还是不够,例如目前 B/S 结构中通常使用的 jsp、传统的 MVC 框架的嵌入等等,对于 jsp 的支持已发布,目前正在测试阶段中,使用 HttpService 适合用于开发较为简单的 B/S 应用,不过以我目前的应用情况来看,我觉得要支持较为复杂的 B/S 应用也是可以的,在我目前的产品的 B/S 部分采用的是一个自主开发的简单的 MVC Framework+Velocity 的结构来开发的,至于到了后台则可以用大家熟悉的各种后台结构方法来实现,采用这种方式的好处是无需应用服务器。

6.8.1.1. 基于 Bridge 方式开发 B/S 应用

而对于需要使用应用服务器特性的应用来说,基于Bridge来开发B/S应用就是另外一种可选择的方法了,在使用这种方法的情况下就可以按照传统的B/S开发方式将系统打包为一个war包放入应用服务器下运行,目前Bridge方式还处于Equinox的server-side incubator中,根据maillist的情况来看,主要是IBM的人现在在试用这个东西,在Equinox网站上提供了一篇如何使用bridge.war来实现这种开发方式的指导文章,在这里我就只简单的说说了,大家感兴趣的话可以把例子中的用户登录验证 模块改为部署到bridge.war里。

bridge.war的使用还是比较简单的,不过目前确实存在着一些bug,从指导文章这个页面中可以找到bridge.war的下载,下载完毕后将它部署到应用服务器(Tomcat、Jetty、Jboss、Websphere、Weblogic等)中,启动应用服务器,启动完毕后就可以直接在应用服务器的console中直接操作OSGI框架的管理console了,可以输入已经熟悉了的ss、start、stop、install等命令,通过这样的方式就可以把Bundle部署到Bridge 这个Web应用中了,在指导文章页面中还可以找到sample.http和sample.http.registry两个例子,这两个例子向我们展示了如何增加新的servlet、资源文件(页面文件、图片、js等)到web应用中去,两个例子功能相同,但采用的方法不同,一个是采用我们已经熟悉了的HttpService去注册新的servlet以及资源文件的方法,另一个则是采用Equinox对OSGI增强的扩展点的方法,通过扩展点的方式的好处在于无需通过 HttpService来注册Servlet和资源文件,从这个例子中也可以简单的看出如何在 Equinox中使用扩展点,需要注意的是在使用这个扩展点的Bundle的例子时必须要保留其中的plugin.xml,并且该Bundle需要先安装到OSGI框架中再重新启动方能生效。

6.8.2. C/S

基于 OSGI 开发 C/S 结构的系统和开发传统的 C/S 结构系统没什么很多不同的地方,只需将系统中所有的模块编写为 Bundle 的方式进行部署,并通过启动 OSGI 框架来启动整个系统。

6.8.3. 嵌入式

 在嵌入式结构的系统方面,OSGI 是绝对的强项,OSGI 的诞生就是为嵌入式系统提供支撑的,所以在嵌入式系统方面采用 OSGI 没有任何的问题。 综合对于 B/S、C/S、嵌入式这三种体系结构系统的支持,可以看出对于 C/S、嵌入式系统而言采用 OSGI 框架没有什么太多的问题,对于 B/S 结构的系统而言其实大部分情况下是可以通过采用 HttpService 来实现的,实在不行的话可以采用 Equinox 提供的 server-side incubator 中的 bridge.war 来实现,相信随着 OSGI 在 Server-Side 应用和企业 应用中的不断发展,对于 B/S 结构应用的支撑将会越来越优秀。

6.9. 注意事项

在使用 Equinox 时,特别要注意的是需要使用 classloader 去加载资源文件的时候,由于每个 Bundle 拥有独立的 classloader,而有些开源框架会采用使用顶级 classloader 去加载文件的现象,这个时候就会导致在 equinox 中运行时会出错,如在 equinox 使用 spring 时,如配置文件中采用了classpath:file 这样的方式去加载其他的配置文件的话,就会出现找不到文件的现象,这个错误就是由于 classloader 引起的,关于 Classloader的问题在后续的章节中将会详细的进行讲解。

另外要注意的就是包的命名问题,如果引用了其他 Bundle Export 的 package,那么在当前 Bundle 中就不能再建同样的包了,举例说:

在 LDAPValidatorBundle 中 引 用 了 UserValidatorBundle Export 的 org.riawork.demo.service.user,那么在 LDAPValidatorBundle 中就不能再采用这个包名 来编写类了,可以看到附件中的实现 Validator 接口的代码的包名是 org.riawork.demo.service.user.impl , 大 家 可 以 把 这 个 包 名 重 构 为 org.riawork.demo.service.user,,再次启动后会发现 LDAPValidatorBundle 的状态为 RESOLVED,而不是其他 Bundle 的 ACTIVE 状态,这个时候通过命令行 start 3 之类的方法启动 LDAPValidatorBundle 就会报找不到类的错误,在实际使用 Equinox 进行项目开发时要注意这个问题。

在使用 Equinox 时可能还会碰到这样那样的问题,在碰到问题的时候可以到 equinox 查找相关的文档,或问使用过 equinox 的同行们,或发邮件到 equinox 邮件组中进行询问。

查看评论