OSGI进阶:第二章 基于 OSGi 的留言板

 由  ValRay 发布

2. 基于 OSGi 的留言板

2.1. 需求

此留言板主要为展示基于 OSGi 的 B/S 系统的通用设计和实现方法,因此功能方面只是一些基础功能的需求: 分页浏览留言 进入留言板的首页时,以每页 20 条的列表的方式列出系统中所有的留言,并提供下一页、上一页、首页和末页的链接导航; 留言列表按留言时间顺序显示,新的留言显示在前; 每条留言显示的内容分别为:留言内容、留言人和留言时间。 新增留言 进入留言板的首页后,用户通过点击新增留言进入新增留言页面; 在新增留言页面中填入留言人、留言内容,此两项均为必填项,填写完毕后点击保存完成新增留言动作,或点击返回放弃新增留言。 删除留言 管理员首先进入留言板的管理功能的登录页面,输入管理员用户名和密码登录管理页面; 管理页面和分页浏览页面相同,只是在每条留言的后增加了删除留言的链接。 另外,此留言板还要求具备可扩展性,如增加编辑留言功能、回复留言功能、搜索留言功能等。

2.2. 基于OSGi的留言板的设计

2.2.1. OSGi 框架的基础功能和设计思想

此留言板基于 OSGi 而实现,我们可以把 OSGi 看做为一个框架(尽管实际上它不仅仅是框架),此框架提供的基础功能有: 支持模块化的动态部署 基于 OSGi 而构建的系统可以以模块化的方式(例如 jar 文件等)动态的部署至框架中,从而增加、扩展或改变系统的功能。 要以模块化的方式部署到 OSGi 中,必须遵循 OSGi 的规范要求,那就是将工程建设为符合规范的 Bundle 工程(就是 Eclipse 中的插件工程),或使用工具将工程打包成符合规范的 Jar 文件,在后续的章节中会介绍如何将一个传统的 Java 工程打包生成为符合 Bundle 要求的 Jar 文件,从而顺利的部署到 OSGi 上运行。

支持模块化的封装和交互 OSGi支持模块化的部署,因此可以将系统按照模块或其他方式划分为不同的Java 工程,这和以往做 Java 系统时逻辑上的模块化是有很大的不同的,这样做就使得模块从物理级别上隔离了,也就不可能从这个模块直接调用另外模块的接口或类了,那么这个时候应该怎么去实现模块间的互相调用呢? 根据 OSGi 规范,每个工程可通过声明 Export-Package 对外提供访问此工程中的类和接口,也可通过将需要对外提供的功能声明为 OSGi 的服务实现面向接口、面向服务式的设计。 还有一种方法来实现模块间的交互,那就是基于 OSGi 的 Event 服务来对外发布事件,订阅了此事件的模块就会相信的接收到消息,做出处理。

支持模块的动态配置 OSGi 通过提供 Configuration Admin 服务来实现模块的动态配置和统一管理,基 于此服务各模块的配置可在运行期进行增加、修改和删除,所有对于模块配置的 管理统一调用 Configuration Admin 服务接口来实现。

支持模块的动态扩展 基于 OSGi 提供的面向服务的组件模型的设计方法以及 OSGi 实现框架提供的扩展点方法可实现模块的动态扩展。 要使用 OSGi 框架提供的这些基本功能,在设计系统时就要遵循 OSGi 框架的设计思 想: 模块化的设计 模块化的设计已经是大家在做系统设计时遵循的基本设计原则,但只有基于OSGi 来做模块化的时候才会真正的体验到何谓模块化 ,因为OSGi中的模块化是物理隔离的,而不基于OSGi的话很难做到物理隔离方式的模块化实现,也就很难使系统真正的做到模块化,通常切换到基于OSGi后就会发现以前的模块化设计做的还是很不足。 基于 OSGi 进行模块化的设计时和传统做设计时的模块设计并没有多大的差别,均为定义模块的范围、模块对外提供的服务和所依赖的服务,相信大家在这点上很容易适应,在 OSGi 中只是更为的规范,更为的遵循面向服务的设计思想。

在 OSGi 中模块由一个或多个 Bundle 构成,模块之间的交互通过 Import-Package、 Export-Package 以及 OSGi Service 的方式来实现。

图表 2 OSGi模块及交互模型

面向服务的组件模型的设计

面向服务的组件模型(Service-Oriented Component Model)的设计思想是 OSGi 的核心设计思想,OSGi 推崇系统采用 Bundle 的方式来划分,Bundle 由多个 Component(组件)来实现,Component 通过对外提供服务接口和引用其他 Bundle 的服务接口来实现 Component 间的交互。 从这个核心的设计思想上可以看出,基于 OSGi 实现的系统自然就是符合 SOA 体系架构的。 在 OSGi 中 Component 以 POJO 的方式编写,通过 DI 的方式注入其所引用的服务,以一个标准格式的 XML 描述 Component 引用服务的方式、对外提供的服务以及服务的属性。 动态化的设计 动态化的设计是指系统中所有的模块均需支持动态的插拔和修改,系统的模块需 要遵循对具体实现的零依赖和配置的统一维护(基于 Configuration Admin 服务),在设计时要记住的是所依赖的OSGi 服务或Bundle都是有可能动态的卸载或安装的。

对于模块的动态插拔和修改,OSGi 框架本身提供了支持,模块可通过 OSGi 的 Console(命令行 Console、Web console 等)安装、更新、卸载、启动、停止相应 的 Bundle。

为保持系统的动态性,在设计时要遵循的原则是不要静态化的依赖任何服务,避免服务不可用时造成系统的崩溃,另外保证系统的“即插即用,即删即无”。 可扩展的设计 OSGi 在设计时提倡采用可扩展式的设计,即可通过系统中预设的扩展点来扩充系统的功能,有两种方式来实现: 引用服务的方式 通过在组件中允许引用服务接口的多个实现来实现组件功能的不断扩展,例如 A 组件的作用为显示菜单,其通过引用菜单服务接口来获取系统中所有的菜单服务,此时系统中有两个实现此服务的组件,分别为文件菜单组件和编辑菜单组件,那么 A 组件相应的就会显示出文件菜单和编辑菜单,而当从系统中删除编辑菜单的组件时,A 组件显示的菜单就只剩文件菜单了,如此时再部署一个实现菜单服务接口的视图菜单组件模块到系统中,那么显示出来的菜单则会为文件、视图。

定义扩展点的方式 按照 Eclipse 推荐的扩展点插件的标准格式定义 Bundle 中的扩展点,其他需要扩展的 Bundle 可通过实现相应的扩展点来扩展该 Bundle 的功能。 系统对于可扩展性的需求很大程度会影响到 Bundle 的划分和设计,这需要结合实际情况来进行设计。

2.2.2. 留言板的设计

留言板的设计遵循 OSGi 的设计思想以及职责单一的原则而完成。

2.2.2.1. 系统层次划分

首先根据留言板的体系结构来确定系统层次的划分。 根据留言板的功能需求,此留言板为一个 B/S 系统,对其功能需求进行简单分析可以看出此系统基本不涉及业务逻辑处理,因此在系统的层次上简单的划分为页面层 +持久层两个部分,页面层负责信息的显示以及页面操作的提供;持久层负责信息的存储和获取。

2.2.2.2. 系统模块划分

根据留言板的功能需求并结合系统层次来划分系统模块,确定各模块对外提供的服务以及需要引用的服务。 模块划分的粒度没有明确的指导法则,需要根据设计师们的经验来确定,基本的原则是职责单一的原则,由于留言板系统本身的功能较为简单,在模块的划分上就按照功能职责来划分,每个模块中直接包含了页面层+持久层的实现,不再划分为 页面层的 Bundle 和持久层的 Bundle,按照留言板的功能需求将系统的模块划分为: 留言列表模块 此模块负责实现将留言从数据库中分页提取,并显示至页面中。 无对外提供的接口,同时也不需要引用外部的接口。

新增留言模块 此模块负责提供留言编写页面,在用户提交后将留言信息存入数据库中。 无对外提供的接口,同时也不需要引用外部的接口。 管理员登录模块 此模块负责提供登录页面,在用户提交后查询数据库确认用户是否能够登 录。

无对外提供的接口,同时也不需要引用外部的接口。 删除留言模块 此模块负责提供根据留言序号从数据库中删除留言的功能。 无对外提供的接口,同时也不需要引用外部的接口。 功能模块的划分就是这么设计了,另外还需考虑的就是模块的部署问题了,在传统的 B/S 开发模式下,我们是采用将整个 web 应用打包为 war 的形式部署至应用服 务器下,在 OSGi 中则通过 OSGi 的 Http Service 或通过 Equinox 的 Bridge 方式和 web 应用服务器集成来部署为 Web 应用,对于留言板系统我们选用直接通过 OSGi Http Service 来注册 web 应用的方法。

OSGi 通过 Http Service 将相应的 Servlet 以及资源文件绑定至相应的路径的请求上,当访问此路径时,Http Service 会转发到相应的 Servlet 进行处理,为了避免编写 Servlet 以及页面的复杂性,在此可编写一个简单的 MVC 框架,提供一个公用的 Servlet 来作为 Controller,同时允许每个 web 应用对此 servlet 进行扩展覆盖,Servlet 将请求转发给相应的响应类,响应类处理完毕后可直接返回相应的页面地址,页面采用 Velocity 的模板方式来进行编写,因此在模块上需要增加一个简单的 MVC 框架模块。

2.2.2.3. 系统模块设计

从“即插即用、即删即无”的部署角度来看需要增加什么新的模块来支持现有的功能模块的部署: 部署留言列表模块,此时访问留言板的路径即可查看到留言列表; 部署新增留言模块,此时可在留言列表页面的右上角看到新增留言链接,点击新增留言链接进入新增留言页面,点击保存后返回到留言列表页面; 部署管理员登录模块,此时可通过留言板路径/admin 进入管理员登录页面,登录成功后进入留言列表页面; 部署删除留言模块,当以管理员身份登录后在留言列表中的每条留言后可看到删除按钮,点击删除即可删除相应的留言。 从部署角度上可以看出,系统就像是一颗树般的慢慢长出来了,形式的图示如下:

在分析完系统的部署方式后,结合各模块的功能要求以及 OSGi 的设计思想来进行系统模块的设计,与数据库的操作基于一个封装了 Hibernate 的通用 Bundle 来实现,这个 Bundle 在后续的章节中将详细的介绍,在此就不多解释了: 简单的 MVC 框架模块 此模块需要简单的实现MVC模式,同时对外提供Controller类以及Command 接口的 package,以供外部 Bundle 扩展 Controller 进行自定义的实现,并提供 Controller 以及 Command 注册的扩展点,以供外部 bundle 注册 web 应用 和 Web 响应服务。 做为 MVC 框架需要对外提供通用的 Controller 以及 Command 接口,并确定 Web 请求整个交互过程的方式。

通用 Controller 类的功能为: 通用 Controller 为一个 Servlet 类,负责接收 Web 请求转发至相应的 Command 类,并执行 Command 类将返回的结果 HTML 进行显示,同时提供一些方法供外部覆盖,如默认的 Command 名等。 Command 接口: 传入 HttpServletRequest 和 HttpServletResponse 作为参数,返回 HTML 字符 串,方法示意如下:

public String execute(HttpServletRequest request,HttpServletResponse response) throws Exception;

同时为了 Controller 以及 Command 的注册,需要编写 Controller 的注册管理组件以及 Command 的注册管理组件。 Controller 的注册管理组件:

负责注册系统中扩展出的 Controller 以及相应的资源文件的访问路径,在这里 Controller 采用扩展点的方式来实现,Equinox 已提供了这块的扩展点,在 此处直接使用 org.eclipse.equinox.http.registry。 Command 的注册管理组件: 负责注册系统中的 Command,以便 Controller 能够根据请求调用相应的 Command 实现类来完成请求的响应,在这里 Command 的注册管理基于 OSGi Declarative Services 来实现,因此不需要编写注册管理组件。 根据上述分析,简单 MVC 框架模块的类图如下:

留言列表模块

按照需求新增留言模块安装上时,需要在留言列表页面的右上角产生新增留言的功能链接,相应的当新增留言模块卸载时,新增留言的功能链接也相应的从留言列表上删除;删除留言模块安装上时,如当前用户为管理员,则需在留言列表中增加删除留言的功能链接,相应的当删除留言模块卸载时,删除留言的功能链接也应从留言列表上删除,这就是所谓的“即插即用,即删即无”的实际例子的体现。 按照传统方式,这些功能链接都是采用直接在留言列表页面上的,但现在采用物理分离的模块化的方法来做后,就会发现直接写在页面上的方式造成了留言列表模块对于新增留言模块、删除留言模块的强耦合,而 OSGi 应用的 “即插即用,即删即无”则很好的完成解耦合,基于 Equinox 提供的扩展点的方式可很方便的实现上述扩展的需求,因此在留言列表功能的基础上需增加一个右上角功能的扩展点以及留言列表中功能链接的扩展点。 根据系统分层方法以及留言列表的功能需求,留言列表模块分为获取留言列表 Command 服务、从数据库中获取留言列表服务类以及留言列表显示页面。 留言列表模块作为留言板系统的入口页面,需要注册留言板系统的 Controller,此 Controller 相应的继承 MVC 框架中的 Controller 类进行实现, 并注册为扩展点实现即可。 留言列表模块同时需要对外提供右上角功能链接的扩展点、留言列表功能链接的扩展点,右上角功能链接的扩展点对外提供 html 片段的扩展属性,留言列表功能链接的扩展点对外提供 html 片段以及角色的扩展属性。 留言列表模块的类图如下所示:

新增留言模块

按照系统分层方法以及新增留言的功能需求,新增留言模块分为新增留言页 面、保存留言 Command 服务和保存留言服务。 在功能的基础上需增加一个留言列表右上角扩展点的实现,以将功能挂接到留言列表上。 新增留言模块的类图如下所示:

管理员登录模块

按照系统分层方法以及的管理员登录功能需求,管理员登录模块分为管理员 登录页面、登录 Command 服务。 管理员登录模块的类图如下所示:

删除留言模块

按照系统分层方法以及删除留言的功能需求,删除留言模块分为删除留言 Command 服务以及删除留言服务类。 在功能的基础上需增加一个留言列表功能链接扩展点接口的实现,以将功能挂接到留言列表的每条留言的功能链接上。 删除留言模块的类图如下所示:

留言板所需的扩展功能的支持 基于之上的留言板设计,已可支持回复留言和编辑留言的功能的扩展,搜索留言方面则需留言列表模块增加左上角功能的扩展点定义。

2.3. 基于OSGi的留言板的实现

按照之上的设计来完成留言板的实现.

2.3.1. 环境准备

在《OSGi 实战》Opendoc 中详细的介绍了开发 OSGi 系统的工具箱的准备方法,因此在此篇 Opendoc 中就不再详细介绍了,实现留言板系统所需的 Bundle 有: org.eclipse.osgi org.eclipse.osgi.services org.eclipse.equinox.ds org.eclipse.equinox.http org.eclipse.equinox.log org.eclipse.equinox.servlet.api org.eclipse.equinox.registry org.eclipse.equinox.http.registry org.eclipse.equinox.common HibernateModule 点击 Run,在 Equinox OSGi Framework 新建一个 bulletin 的应用,在 plugins 中选择上述的 bundle,点击 Apply 即可,效果类似如下:

之后编写完模块时可通过启动此 bulletin 应用来启动留言板系统。

2.3.2. 简单的 MVC 框架模块

在准备好环境后我们就可以开始第一个模块的实现了,由于留言板系统是以简单的 MVC 框架模块为树根,慢慢的生长而形成,首先来实现简单 MVC 框架模块。 以 Plugin-Project 的方式创建简单 MVC 框架模块工程; 在Import Packages中增加javax.servlet、javax.servlet.http以及org.osgi.framework; 新建 WebCommand 接口:

/** 
*   响应页面请求 
*   
*   @param request 
*   @param response 
*   @return String HTML片段 
*   @throws Exception 
*/ 

public String execute(HttpServletRequest request,HttpServletResponse response) throws 
Exception;

新建 SimpleMVCFrameworkActivator 类,为 AbstractController 提供获取 BundleContext 的方法;

新建 AbstractController 类,该类中重要的部分就是根据请求参数中的 command 来确定调用相应的 COMMAND 服务,调用方法如下所示:

BundleContext bc=SimpleMVCFrameworkActivator.getContext();       
if(bc==null)    
    throw new Exception("BundleContext不可用");       
ServiceReference[] 
serviceRefs=bc.getAllServiceReferences(WebCommand.class.getName(), 
"(command="+action+")");          

if(serviceRefs.length==0){           
    throw new Exception("系统中没有相应的Command服务或服务不可用"); 
}  如右上角功能链接的扩展的页面显示       

if(serviceRefs.length>1){           
    throw new Exception("系统中对应此Command的服务超过1个"); 
} 
command=(WebCommand) bc.getService(serviceRefs[0]);

编写完上述类和接口后,在 MANIFEST.MF 中 Runtime 的 Export Packages 中增 加 cn.org.osgi.opendoc.bulletin.mvc 和 cn.org.osgi.opendoc.bulletin.service,以供其 他 Bundle 调用 AbstractController 类和 WebCommand 接口。

2.3.3. 留言列表模块

留言列表模块作为留言板系统的入口,需要完成留言板应用 Controller 的编写和注册,首先来完成此部分: 在 MANIFEST.MF 的 Import Packages 中导入 cn.org.osgi.opendoc.bulletin.mvc、 cn.org.osgi.opendoc.bulletin.service 、 javax.servlet 、 javax.servlet.http 、 org.eclipse.equinox.http.registry;

编写 DefaultController 类作为留言板系统的 Controller,继承 AbstractController 抽象类,实现其中的抽象方法;

注册 DefaultController 到 Equinox HttpService 提供的扩展点;

注册此扩展的方法比较简单,在留言列表模块的工程下新增 plugin.xml 文件, 在 plugin.xml 文件中增加如下内容:

<plugin> 
<extension point="org.eclipse.equinox.http.registry.servlets"> 
<servlet  alias="/bulletin"  class="cn.org.osgi.bulletin.mvc.controller.DefaultController"/>   
</extension> 
</plugin>

这样就完成了将 DefaultController 注册到 HttpService,启动留言板应用系统后 HttpService 就会调用 register 方法将此 Controller 进行注册,当访问/bulletin 时,请求就会转发到 DefaultController 类处理。

接下来需要完成的为留言列表 Command 服务和留言列表服务: 要实现留言列表服务,首先需要完成留言对象的编写,按照 Hibernate 的格式要求编写留言对象,并生成 hbm 映射文件; 将留言对象注册到 Hibernate 封装模块提供的扩展点,起到的效果相当于在 hibernate.cfg.xml 中加了:

<mapping resource=”cn/org/osgi/bulletin/po/Bulletin.hbm.xml”/>

模块化的特点就是不会侵入到其他的模块,只通过扩展等方法来扩展其他的模块的资源和功能等。

注册留言对象只需在 plugin.xml 中增加以下内容:

<extension point="HibernateModule.HibernateExtension">         
<po class="cn.org.osgi.bulletin.po.Bulletin"/> 
</extension>

编写完毕后在 MANIFEST.MF 的 Runtime 的 Export Packages 中增加 cn.org.osgi.bulletin.po 包的导出。

经过上面的这两步后启动留言板应用时 Hibernate 封装模块即可加载留言对象 到 Hibernate 的管理范围内。 编写留言列表服务,留言列表服务基于 Hibernate 封装模块 Export 的 CommonDaoService 实现; 编写留言列表 Command 服务,采用 Declarative Services 支持的 bind 的方式注 入留言列表服务,实现分页获取留言列表数据,并将列表数据填入 VelocityContext 中,以供页面进行显示; 基于 Velocity 方式编写留言列表模板页面; 编写 Declarative Services 所需的服务组件的描述文件,将留言列表服务和留言列表 Command 服务列入 Declarative Services 的管辖范围,在编写留言列表 Command 服务的描述文件时,需要加上 Controller 调用 Command 服务时使用 的标识符,如:; 将服务组件描述文件加入到 MANIFEST.MF 的 Service-Component 中。

留言列表模块还需提供对于留言应用而言的新功能以及对于留言本身操作的新功能的扩展点,以便对此两部分的功能进行扩展,在这里就需要学习如何编写 Equinox 的扩展点了: 定义扩展点的方法并不复杂,进入 MANIFEST.MF 中的 Extension Points 标签项,点击其中的 Add 按钮,输入扩展点的 ID、Name 以及 Schema,Schema 如何定义也就决定了其他模块要进行扩展时如何描述了,在这里以对于留言应用而言 的新功能的扩展点为例,将其 ID 设置为 RightCorner、Name 设置为 RightCorner、 Schema 设置为 schema/RightCorner.exsd:

设置完毕后在 plugin.xml 可看到如下信息: 在留言列表模块工程的目录下建立 Schema 目录,在此目录下新建 RightCorner.exsd,双击打开进入如下视图:

填入相应的信息; 点击标签项中的 Definition 标签项,进入如下视图: ![](http://assets.osgi.com.cn/article/7289427/无标题234234.png) 在上面的视图中点击 New Element,将此 element 的名称修改为 snippet; 选中 snippet 元素,点击 New Attribute,在其中增加 html 和 role 两项 attribute,将 html 的 Use 选项设置为 required,Type 设置为 string;role 的 Use 选项设置为 optional,Type 设置为 string; 选中 extension 元素,右键选择其中的 New—Compositor—sequence,将 sequnece 中的 Max Occurences 设置为 Unbounded; 选中 sequence,右键选择其中的 New—Reference—snippet,将 snippet 中的 Max Occurences 设置为 Unbounded; 在配置完上面的步骤后,后形成的 Definition 视图如下所示:

经过上面的步骤后,即完成了 RightCorner 扩展点的配置工作,按照类似的方法配置 Bulletin 扩展点,在该扩展点下增加 snippet 元素,其中的 Attribute 分别为 title、 command 和 role,其中 title 和 command 为 required 项。

在配置了 RightCorner 和 Bulletin 扩展点之后,就是如何去获取这两个扩展点的扩展 了,根据 Equinox 对于扩展点的使用方法,可通过 Equinox 提供的 IExtensionRegistry 服务来获取,同时通过实现 IRegistryChangeListener 来注册为扩展注册变化的监听 器,这样当实现扩展点的扩展发生变化时可动态的获取到这些变化的信息,以便相应的做出处理,结合设计编写获取留言板列表扩展的服务,在编写该服务时,重要的是掌握如何获取扩展点对应的扩展以及如何监听扩展点对应的扩展发生的变化,以保证系统的动态性: 获取相应扩展点的扩展;

通过 IExtensionRegistry 服务获取: IExtensionRegistry.getExtensionPoint( 扩展点所在插件的 ID+”.”+ 扩展点 ID).getExtensions();

通过这样的方法即可获取到相应扩展点的扩展,例如对于 RightCorner 扩展点,获取其扩展实现的方法为: IExtensionRegistry.getExtensionPoint(“BulletinListModule.RightCorner”).getExten sions(); 监听相应扩展点的扩展发生的变化;

实现 IRegistryChangeListener 接口的 public void registryChanged(IRegistryChangeEvent event)方法,通过 IExtensionRegistry 将当前类注册为扩展点变化的监听器,在注册时使用的 NAMESPACE 参数为扩展点所在的插件的插件 ID,例如对于 RightCorner 扩展点而言即为:

IExtensionRegistry.addRegistryChangeListener(this,”BulletinListModule”) 在 registryChanged 方法中通过 event.getExtensionDeltas 来获取所感兴趣的扩展 点的扩展的变化情况,如 RightCorner 扩展点的扩展的变化的监听:

event.getExtensionDeltas(“BulletinListModule”,”RightCorner”);

在获取到 IExtensionDelta 后,通过其 getKind 方法来判断扩展发生了何种变化,如 getKind()==IExtensionDelta.ADDED,那也就意味着系统中增加了一个此扩展点的实现;如 getKind()==IExtensionDelta.REMOVED,那也就意味着之前的一 个扩展点的实现已经从系统中删除了。 通过这样的方法即可动态的获取和处理系统中扩展点的扩展实现,在留言表列表模 块中编写了一个 BulletinListExtensionComponent 来专门处理扩展点扩展实现的获取: 在激活组件时获取到当前扩展点扩展实现的集合,并注册监听器;

public void activate(ComponentContext context){ IExtension[] 
extensions=registry.getExtensionPoint(NAMESPACE+"."+RIGH TCORNER_EXTENSIONPOINT).getExtensions();   
for (int i = 0; i < extensions.length; i++) {    
    rightCornerExtensions.add(extensions[i]); 
} 
registry.addRegistryChangeListener(this,NAMESPACE);

} 在扩展点扩展实现发生变化时,相应的处理扩展点实现的集合;

**public void** registryChanged(IRegistryChangeEvent    event) { 
IExtensionDelta[] 
deltas=event.getExtensionDeltas(NAMESPACE,RIGHTCORNER_EXTENSIONPOINT); 
for (int i = 0; i < deltas.length; i++){     
    switch (deltas[i].getKind()){       
        case IExtensionDelta.ADDED: 
            rightCornerExtensions.add(deltas[i].getExtension()); 
            break;       
        case IExtensionDelta.REMOVED: 
            rightCornerExtensions.remove(deltas[i].getExtension());       
            break;       
        default:       
            break; } 
} 
}

在编写完扩展点的管理服务后,就需要把这些扩展点的扩展表现到留言列表页面上了,这步较为简单,在留言板列表模块的 Command 服务中调用留言板列表扩展点管理服务,获取扩展点的扩展实现填充至 VelocityContext 中,页面获取到扩展中的相关属性配置完成显示即可,如右上角功能链接的扩展的页面显示:

\#if($rightCornerExtensionsHtml.size()>0)   
<table id="normaltable" cellspacing="0" align="center"> 
<  tr height="23"> 
<td align="right"> 
\#foreach($html in $rightCornerExtensionsHtml) 
&nbsp;$html
\#end 
</td> 
</tr> 
</table> 
\#end

通过上述步骤,留言列表模块即宣告完成,这个时候启动留言列表应用,通过 http://localhost:Web端口/bulletin访问到此应用(此处的Web端口默认为 80,可通过在 vm arguments中增加-Dorg.osgi.service.http.port=8080 来自定义web端口)。

2.3.4. 新增留言模块

新增留言作为对于留言板功能的扩展,需要作为扩展实现挂接到留言列表页面中,新增留言模块的实现较为简单,在这里主要讲解下如何使用留言列表模块中提供的 RightCorner 扩展点: 要使用 RightCorner 扩展点,只需在 plugin.xml 中加入以下内容:
在编写完新增留言模块需要的新增留言 Command 服务、保存留言 Command 服务、新增留言 Velocity 模板页面后,再次启动留言板应用,这个时候在留言板列表页面的右上角,可看到新增留言链接,通过卸载新增留言模块来测试下扩展点的动态效 果,在 console 中 uninstall 新增留言模块,刷新留言板列表页面,此时新增留言链接也从右上角删除了,真正的做到了“即插即用、即删即无”的效果。

2.3.5. 管理员登录模块

作为演示程序,在此处管理员登录的校验直接写为了固定值,管理员登录模块和新增留言模块的编写相似,在此不多描述。

2.3.6. 删除留言模块

删除留言模块和之前的新增留言模块不同的地方仅在于使用的扩展点不一样,删除 留言模块使用的为 Bulletin 这个扩展点,在 plugin.xml 中增加以下内容即可实现对此扩展点的扩展:

<extension point="BulletinListModule.Bulletin"> 
<snippet command="DELETEBULLETIN" role="admin" title="删除"/></extension>

在编写完上面的模块后,通过http://localhost:web端口/bulletin端口/bulletin?command=ADMINLOG来登录,大 家可尝试将管理员登录这个功能挂接到留言板列表上去,就像新增留言的功能链接 一样。

2.4. 小结

内容总结 本章节以一个简单的留言板系统为例,介绍了 OSGi 框架的功能、设计思想,遵循 OSGi 的设计思想完成留言板系统的设计,并基于设计实现了此留言板系统。 本章中所展示的模块化的实现方法也不一定是佳的方法,本章中所展示的模块化的方法适合于分模块进行开发的团队,但并不适合分层开发的团队,如果团队采用分层开发的方法,那么在模块的划分上好是在现有基础上再次划分(即把模块按层次再划分为几个 Bundle,而不是统一的一个 Bundle),以提高团队协作的效率。 如果你有想使用 OSGi 的想法了,估计看完本章后还是会有不少的疑问,例如基 于 OSGi 怎么来构建基于 webwork+spring 2+hibernate 的系统、能否把现有系统改 造为基于 OSGi 的系统呢,很高兴后面的章节将解答你的这些疑问。 知识点 从这个章节的内容中我们学习到了基于 OSGi 如何设计/实现一个规范的模块化,动态化以及可扩展的系统,希望这些实践方法能为大家在基于 OSGi 设计/实现 OSGi 系统提供一些指导,以更好的实现规范的模块化、动态化以及可扩展的系统,大家可以去想想,如果不基于 OSGi,要实现一个符合这样需求的系统要怎么去做呢,如果觉得比 OSGi 复杂的话,那么不妨试试基于 OSGi 来实现你的系统。

在本章中还提及到了树状设计实践方法,这个设计实践方法有利于保证 OSGi 应用保持模块化、动态化以及可扩展性,并形象化的将系统的模块化、扩展性以及耦合情况描述出来。 衍生发展 本章节主要是为了展示 OSGi 的设计思想以及基于 OSGi 的设计思想如何设计实际的系统,因此在留言板的功能等方面还有很多可改进的余地,大家可以基于源码来进行重构、扩展(例如增加编辑留言功能、回复留言功能等等)或编写一个新的基于 OSGi 的更好用的留言板,希望这个留言板系统能成为 OSGi 的一个很好的 Demo,充分的展示出 OSGi 的优势,就像 java ee 的 petstore 一样。 同时在本章节所产生的 MVC 框架只是一个非常简单的 MVC 框架,大家可以进行改造来提升这个 MVC 框架的功能,如支持将 HttpServletRequest 绑定到对象上,使得 WebCommand 服务能脱离 Servlet 环境等。


ccfeng 2014-04-28 10:21

不错~

顶(0) 踩(0) 回复
查看评论