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

 由  ValRay 发布

6.3. 开发 Bundle

一切准备工作完毕,开始 OSGI 实战之旅,首先来开发我们的第一个 Bundle,按照设计,根据依赖性,首先来做用户登录验证接口 Bundle:

●第一步:建立 Bundle 工程 ■在 Eclipse 中通过建立 Plug-in 工程来建立 Bundle 工程;

6.3.1

■输入工程的相关信息,这里和建立普通 JAVA 工程唯一不同的就是需要选下 this plug-in is targeted to run with,在这里选择 an OSGI Framework 的 standard 选项,也就是说建立的是标准的 OSGI Bundle 工程.

6.3.2

■输入 Bundle 的相关元数据信息; Plug-in ID 指的是 Bundle 的唯一标识,在实际的项目中可以采用类似 java 的包名组织策略来保证标识的唯一性; Plug-in Version 指的是 Bundle 的版本; Plug-in Name 指的是 Bundle 的更具有意义的名称; Plug-in Provider 指的是 Bundle 的提供商; Classpath 指 Bundle 运行时的类路径,由于在建立工程时 Output folder 设置的 bin,在 classpath 这里同样设置为 bin;

剩下的关键的就是 Activator 这个部分了,这里填入自己的一个类名就可以了,在工程建立时 Eclipse 会自动的建立这个类。

6.3.3
完成 Bundle 工程的建立,在 package 视图中可以看到类似这样的视图,表明工程建立成功了。

6.3.4
●第二步:对外提供用户验证接口 package ■建立一个 Validator Interface;

6.3.5
■对外提供用户验证接口 package,在之前的介绍中已经提及在 OSGI 框架中通过 Export-package 元数据来标识 Bundle 对外提供的 package,双击META-INF 下的 MANIFEST.MF,选择其中的 Runtime 标签项,在 Exported Packages 中点击 add,在弹出的窗口中选择所建立的 Validator 接口所在的package:org.riawork.demo.service.user,保存就完成这步了。

6.3.6
在完成后可以去看看 MANIFEST.MF 文件的内容,会看到其中有这项:
Export-Package: org.riawork.demo.service.user

由于用户登录验证接口Bundle在启动时和停止时都不需要做什么处理,因此无需对 UserValidatorActivator 做什么改动,经过上面两个步骤后,就完成了用户登录验证接口Bundle的开发了,是不是挺简单的呢,可以看到在 Bundle 的开发中与普通 Java 工程的开发基本相似,不同点在于需要使用 Bundle的元数据信息,OK,继续来完成其他几个Bundle的开发。

按照之前建立 Bundle 工程的方式分别建立LDAPValidatorBundle 、DBValidatorBundle、ConfigFileValidatorBundle 和UserValidatorWebBundle,建立后如下图所示:

6.3.7

●LDAPValidatorBundle

■第一步:在 Bundle 元数据中引入用户验证接口的包,这样在 Bundle 中才可去实现接口,这里可以看出 Bundle 开发和传统的方式不同的地点,在传统的方式下就需要引用这个接口的 jar 文件或者引用接口所在的工程才行。

6.3.8

■第二步:编写实现接口的类,鉴于本文是演示性质,在这里固定了登录的用户名和密码。

●DBValidatorBundle 按照 LDAPValidatorBundle 方式同样实现。

●ConfigFileValidatorBundle

按照 LDAPValidatorBundle 方式同样实现。

●UserValidatorWebBundle

和传统的 Web 开发方式不同,由于 OSGI 框架中并没有象 web 应用服务器那样的 Bundle,就不能象 web 应用直接部署到 web 服务器那样简单了,需要通过 HttpService 将 Servlet 以及资源文件(象图片、css、html 等)进行注册,这样才可访问了,详细在后续的 HttpService 中介绍,在这里暂时就不提及 Service 的使用方法,先搭建好架子,鉴于这是个 Demo,就采用简单的方式实现了,这个 Bundle 由登录的首页面以及响应的 Servlet 构成。

■在 src 目录下建立一个 page 的目录,在其中编写 login.htm;

■登录响应的 Servlet,由于 Servlet 需要继承 HttpServlet,需要引用下 Servlet API,Equinox 在实现 Http Service 时为了大家使用方便,提供了一个 Servlet API 的 Bundle,在准备工具箱时我们已经下了这个 Bundle 了,那么现在我们就可以通过这样来引用 Servlet API:

6.3.9

这样之后就可以开始来编写登录响应的 Servlet,写法和普通的 Servlet 没有任何区别,具体见附件中的代码。

经过这些步骤后,用户登录验证模块所需要的 Bundle 就全部搭建好了,启动一下看看,点 Eclipse 的 Run…,新建一个 Equinox OSGI Framework 的运行工程:

6.3.10
默认已经选中了 workspace 下的 Plugin 工程,点击旁边的 Add Required Plug-ins,保存这个配置(如需要更改启动时的 web 端口,仍然按照之前在准备工具箱时的方法),点击运行,启动正常后可以通过在 osgi>后输入 ss 回车:

6.3.11

OK,到此,用户登录验证模块的 Bundle 开发完成了,经过这些学习后,可以看出 Bundle 的开发并不复杂,和做普通的 java 工程开发唯一不同的是借助使用 Bundle 的元数据来实现工程之间 package 的共享,接下来我们开始学习 Service 的开发、发布以及使用,来完成用户登录验证模块的开发。

6.4. 开发、发布和使用Service

从开发角度来看,Service 有点象虚拟的概念,因为在编写 Service 时和编写普通的 Java 类(POJO)没有任何的区别,其实在上面已经写过三个 Service 类了,分别是LDAPValidatorImpl、DBValidatorImpl 以及 ConfigFileValidatorImpl。 在 OSGI 框架中,Service 是个实际的概念,只有通过 BundleContext 注册成 Service 才能使得一个 POJO 作为 Service 在 OSGI 框架中被使用,同时也只有通过 BundleContext 来获取发布到框架中的 Service,通过 Service 的方式来实现 Bundle 之间实例级的依赖, 和 Import-Package、Require-Bundle 不同的地方在于通过 Service 获取的是其他 Bundle 中类的实例。

来看看怎么样去发布之前写好的几个 Service:

●LDAP 验证 Bundle

打开之前在搭建 LDAP 验证 Bundle 时的 Activator 文件,可以看到 start 和 stop 两个方法,这两个方法就是用来管理 Bundle 的生命周期的,我们需要在 Bundle 启动的时候注册需要发布的 Service,在 Bundle 停止的时候卸载发布的 Service,在 OSGI 框架中通过调用 BundleContext 来注册 Service,方法是这样的:
context.registerService(服务标识名,服务实例,服务实例属性);

方法返回的是 ServiceRegistration,可以通过返回的这个 ServiceRegistration 来卸 载这个 Service,在 stop 方法中通过这样的方法来卸载注册的这个 Service:

serviceRegistration.unregister();

通过这样两个方法后就完成了 Service 的注册和卸载,代码参见附件。

●DB 验证 Bundle

和 LDAP 验证 Bundle 同样的完成 Service 的注册和卸载。

●配置文件验证 Bundle

和 LDAP 验证 Bundle 同样的完成 Service 的注册和卸载。

Service 的发布写好了,开始来使用 Service,根据之前的设计,在这个用户登录验证模块中,只有UserValidatorWebBundle 需要使用到其他 Bundle 提供的 Service,那么就来完成 UserValidatorWebBundle:首先完成之前的登录响应的 Servlet,在这个 Servlet 中需要通过 BundleContext 获取用户登录验证服务,在这个 Servlet 增加一个构造器,以供外部在实例化时传入 BundleContext;在 doGet 方法中增加获取用户登录验证服务的代码,并将验证的结果展现给用户,在 OSGI 框架中通过这样的方法来获取服务:

ServiceReference serviceRef=context.getServiceReference(服务标识名);
Object service=context.getService(serviceRef);

获取到用户登录验证服务实例后,通过调用验证方法获取到用户登录验证的结果,由于目前 Http Service 尚不支持 jsp,在这里就直接在 servlet 中把验证的结果写入 response 返回。

经过这样一个步骤后就完成了登录响应 Servlet 的编写。

●接着来完成用HttpService来将实例化的登录响应的servlet以及登录页面的资源文件注册到 Web 应用中,和传统 web 应用开发不同,这里的 servlet 的实例化是由开发人员来操作的,当然,其实也很简单,就是直接的:

Servlet servlet=new LoginServlet(context);

由于要使用到 HttpService,需要首先在 import-package 导入 org.osgi.service.http包,对于 HttpService 采用监听的方式来获取,也就是说,当监听到 HttpService 可用时,则使用这个 HttpService 来将 Servlet 和资源文件注册,当 HttpService 不可用时,则将 servlet 和资源文件卸载,在 OSGI 框架中,要监听服务的状态的方式也很简单,通过实现 ServiceListener 接口中的 serviceChanged 方法以及将监听器实例注册到 BundleContext 中即可实现,通过BundleContext 将 ServiceListener 注册采用的是这个方法:

context.addServiceListener(ServiceListener 实例,”(objectClass=服务标识名)”);

HttpService 通过这样的方法来注册 servlet:

HttpService.registerServlet(Servlet Mapping Url,Servlet 实例,配置信息,HttpContext 实例);
示例:
service.registerServlet("/demo/login", servlet, null, null);
HttpService 通过这样的方法来注册资源文件:
HttpService.registerResources(资源 Mapping Url,资源文件路径, HttpContext 实 例);
示例:
service.registerResources("/demo/page","page",null);

具体请参见代码。

经过这样两步后就完成了 UserValidatorWebBundle 的开发了。 后按照之前的方法,在 Eclipse Run…中重新点 Add Required Plugins,之后再选中其中的 org..eclipse.equinox.http,然后点击 Run,同样,当在 console 中看到 osgi>则 表明启动成功,再在 osgi>后输入 ss,看看是不是类似如下的画面:

这 个 时 候 可 以 看 到 ConfigFileValidatorBundle 、 DBValidatorBundle 、 LDAPValidatorBundle都启动了[ 在后续StartLevel Service将介绍如何设定StartLevel来控制哪些Bundle启动,哪些不启动 ],如果想现在采用LDAP方式来验证用户的登录,那么可以通过stop命令来停止另外两个Bundle的运行:

stop 11 stop 12
好,通过 web 来访问下用户登录验证模块:

输入用户名和密码后,点击登录,看看是不是会进入一个提示登录结果的页面:

再看看 console 中的提示信息:osgi> LDAP 验证方式 好,这个时候再来动态切换下登录的验证方式,将 LDAP 验证方式切换为 DB 验证方式:
stop 12 stop 13

再登录下,去看看 console 中的提示信息:osgi> DB 验证方式 经过了这些步骤后,完整的实现了用户登录验证模块的需求,当要增加新的验证方式时,只需要按照之上编写LDAPValidatorBundle 的方法同样编写即可。

在对开发、发布和使用Service进行学习后,估计大家会觉得Service的注册和使用过于复杂,确实如此,可能大家都会想为什么不采用DI的方式来解决这个问题,在OSGI R4 中提出了Declarative Services来解决这个问题,Declarative Services不仅仅支持DI 方式,还推出了更为完整的Service-Oriented Component Model,在之后的Declarative Services章节中将会详细介绍,并重新实现Service的注册和使用,到时会看到一个更好用且更加支持动态策略的模型。

查看评论