摘要
本文给出如何将 JSF、DWR、DOJO 集成在一起来创建丰富 Web 应用(该应用使用 Portlet 和 Facelet)的方法。假定读者对这些框架和它们所提供的特性已有基本的了解。
例子应用
本文中所讨论的例子应用是一个产品管理应用。该应用为期用户提供了如下特性:
- 用户可以基于名字搜索一个产品类型
- 当用户选择了一个产品,将显示一个带有分隔面板(split pane)的新窗口。该分隔面板左侧以树的形式(就像你在 Windows Explorer 所看到的那样)显示所有属于该产品类型的产品子类型。可能每个产品子类型还有它自己的子类型。分隔面板右侧显示还有如下标签的标签面板(tabbed pane):
- Products(产品) —— 这个标签页显示属于所选子类型的产品列表
- Add Product Subtype(增加产品子类型) —— 这个标签页显示一个表单,用来接受给所选产品子类型增加新产品子类输入值
- 在分隔面板的底部,有一个 back 按钮以便回到搜索产品类型页。
- 当某个子类型关联的产品列表显示在分隔面板右侧时,每一页应该只显示 10 个产品。这意味着应该能够对屏幕上的结果进行分页和排序。
- 显示在分隔面板左侧的树应该可以展开 / 折叠(expandable/collapsable)而且不应导致页面刷新,这样会增强用户体验。
- 鼠标右键点击树上任何一个节点(代表一个产品子类型)应该给用户显示弹出菜单,带有给所选子类增加新产品子类子节点、删除所选产品子类型、用数据库的最新数据刷新所选产品子类的孩子产品子类列表等选项。
- 当一个新的产品子类被增加到所选产品子类上后,无需刷新页面,新产品子类应该立刻显示在树上。
图 1 显示了当用户选择一个产品类型 / 子类型时,屏幕上的期望效果。
图 1. 产品子类详细信息窗口
DOJO
创建跨浏览器兼容的树结构、标签面板、分隔面板、弹出菜单等等是耗时的工作,同时这些工作最好是由有经验的 Javascript/DHTML 程序员来完成。有许多工具包支持这些 UI 部件并且是跨浏览器兼容的,但是支持丰富事件处理模型的工具包却不多。以下罗列了一些例子应用需要响应以满足用户需求的用户动作:
用户动作 应用程序响应 选择树节点 显示属于该节点的产品子类列表 右键点击树节点 显示带有增加孩子产品子类、删除所选产品子类等功能的弹出菜单 选择标签面板上的‘增加产品子类型’标签 显示输入新产品子类型信息的表单 当用户点击 [+] 号展开树并察看子节点时 从数据库中装载孩子节点信息并显示在树上 DOJO 工具包是一个 Javascript/DHTML 工具包,它提供了一套丰富 UI 部件(包括但不止限于树、标签面板、弹出菜单),该套 UI 部件带有一个非常适合用于本例子应用的丰富事件处理模型。
DWR
DWR ( Direct Web Remoting ) 是一个简化构建用 Java 编写的 AJAX 应用的 AJAX 框架。DWR 提供了许多特性,包括(但不只限于此):
- 由 Java 类(开发者创建用来处理 AJAX 请求)创建 Javascript
- 允许 JSF 管理 bean 作为处理 AJAX 请求的 Java 类
- 用转换器(converter)对 Javascript 关联数组(associative array)和 Java bean 进行双向转换
- 用转换器对 Javascript 数组和 Java 集合进行双向转换
转换器在 DWR 中扮演一个十分重要的角色,并提供了一个更整洁的编程模型。例如,如果用户输入新产品子类信息,并需要应用程序保存它,那么在 Web 层有两种方法获得其信息:
- 用 HttpServletRequest 的 getParameter 方法获得所有关于新产品子类的信息。
- 创建一个 DTO(数据传输对象) ProductSubtype,其所有产品子类属性都有 getter 和 setter。在 dwr.xml 文件中配置一个转换器将 ProductSubtype 指定为一个bean转换器。在 Javascript 中,简单地创建一个关联数组(associative array)并把它传递给 Java 类(由它来处理 AJAX 请求)中以 ProductSubtype 作为参数的方法。在本例中 DWR 将做由 Javascript 关联数组到 ProductSubtype 的转换,因为 ProductSubtype 在 DWR 中被声明用于bean 转换器。
后一种选择提供了一个更整洁的编程模型,你不必处理获取请求参数及创建一个在 Java 程序中被使用的 DTO。
当 AJAX 请求被处理时,大多数时候需要在 Javascript 回调方法中接收状态编码、消息或一些的数据,以决定给用户显示什么、不显示什么。Bean 转换器在这种情景下非常方便。
DOJO 的丰富事件模型结合 DWR 处理 Java 应用 AJAX 请求的整洁方法,提供了创建高交互性 Web 应用(类似于例子应用)的一种途径,应用中由 DOJO 组件产生的事件被传递给 DWR 去处理。
问题描述
- 在一个 Portal 环境中,开发者不负责为用户界面产生 HTML,界面通过解析符合 Facelet 的 XHTML 文件来产生。即使 JSF 管理 bean 的属性包含 HTML 字符串作为其值,它并不是由 Portal 解析的,而是照原样在用户界面上显示给用户。如何产生可以被 Web 浏览器执行以创建 UI 部件的 DOJO 特定 HTML 呢?
- DOJO 提供了丰富事件模型,而且可以在 Javascript 中截获这些事件。DWR 框架可以用来在服务器端接收这些事件,但是服务器端会话状态需要维护在什么地方?
- 当树上的节点超过几百个时,在 IE 或 Mozilla 中 Javascript 创建 DOJO 树节点需要花些时间。因此是否这意味着如果节点超过几百个,应用不能在浏览器使用 DOJO 的树部件?
- 在 Web 应用中使用 AJAX 后,当窗口上发生用户事件时如何产生 HTML 或 HTML 片断?应该在 Java 代码中编码产生还是从一个外部文件获取?应用程序能否在 AJAX 请求的响应中显示复杂用户界面?
- 当使用 AJAX 时,代码可能因使用 HttpServletRequest 的 getParameter(“fieldName”) 方法而变得混乱,难以维护。
解决方案
自定义 JSF 组件
自定义 JSF 组件用来为 DOJO 的树和分隔容器组件产生所需的 HTML。JSF 组件所产生的 HTML 总是由浏览器解析,而不是由 Portal 作为文本输出。下面是 XHTML 的部分内容,展示了如何自定义 JSF 组件用来产生 DOJO 的树和标签面板窗口部件。
<div xmlns="http://www.w3.org/1999/xhtml"<br></br> ...<br></br> ...<br></br> xmlns:dojo="http://dojotoolkit.org/"<br></br> xmlns:mytree="http://mytree.com/tree"<br></br> xmlns:mytab="http://mypane.com/tabPane"><br></br> <ui:composition><br></br> <ui:define name="body"><br></br> <f:view><br></br> <h:form styleClass="form" id="formId"><br></br> <div dojoType="SplitContainer" orientation="horizontal" sizerWidth="5" activeSizing="false" style="overflow:<br></br> auto; whitespace: nowrap; height: 550px; background: transparent; padding: 5px;" ><br></br> <div dojoType="ContentPane" sizeShare="20"<br></br> style="overflow: auto; whitespace: nowrap;"><br></br> <mytree:treeComponent backingBeanName="treeBackingBean"></mytree:treeComponent><br></br> </div><br></br> <div dojoType="ContentPane" sizeShare="80" style="overflow: auto; white-space nowrap;"><br></br> <mytab:tabPaneComponent/><br></br> </div><br></br> </div><br></br> ...<br></br> ...
如果树上的节点数很大(超过 200),自定义 JSF 组件不应为多于 200 的节点产生代码。如果节点数超过了 200,在装载页面时 IE 将花费大量时间去创建这些窗口部件。本文例子应用的自定义 JSF 组件只创建 100 个树节点(在根一级上)并在最后显示‘Show more…’选项。当用户选择了‘Show More…’选项,则由 DWR 负责将剩余的节点信息从数据库中取来。该信息接着被传递给 Javascript 回调方法以便用程序创建 TreeNode 窗口部件。
MyFaces 也提供了能产生 DOJO 树的组件,但是 MyFaces 组件一次性创建所有节点,这不是好方法,因为当树上的节点数达到上千时 Web 应用中的组件将变得无法使用。
DWR 和 JSF
DWR 要求你创建 Java 类并将其配置到 dwr.xml 配置文件中。DWR 创建一个 Javascritp 文件(文件扩展名为.js),该文件的名字是在 dwr.xml 中配置的
如下 dwr.xml 配置信息展示了 Java 类是如何被配置的:
<create creator="jsf" javascript="AjaxBean" scope="request"><br></br> <param name="managedBeanName" value="ajaxBean" /><br></br> <param name="class"<br></br> value="com.somebean.AjaxBean" /><br></br> </create>
creator="jsf"
这一句说明 Java 类被配置为 JSF 管理 bean。该 Java 类包含所有将由 Javascritp 调用的 AJAX 方法。
<param name="managedBeanName" value="ajaxBean" />
这一句说明在 faces-config.xml 配置文件中管理 bean 的名字为 ajaxBean。
<param name="class"<br></br> value="com. somebean.AjaxBean" />
这一句说明所引用的实际 Java 类。
javascript="AjaxBean"
这一句说明在 Javascript 代码中以此名称使用 Java 类。
为了在 Javascript 中使用 AjaxBean,需要用
评论