打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
实现图形JSF组件

  开发人员认为,如果有合适的工具来创建交互式Web界面,他们就能将时间集中在核心需求和定制上,并在规定时间内及时得交付应用程序。与其他技术如JavaServer Pages或Apache Struts 相比,JavaServer Faces (JSF)技术为创建交互式Web应用程序带来了很多便利。JSF在程序逻辑和GUI表示之间划出一条清晰的界限,提高了对Web程序的维护能力,并为Web用户界面组件的开发和重用提供了一个框架。

  如今,许多Web应用程序开发人员都在转而使用JSF,但是他们发现,预先定制的JSF UI组件受到基本DHTML窗口部件的限制。监管或业务流程监控之类的高级应用程序需要能与JSF框架兼容的高级可视化组件。JSF框架的标准化使它易于开发能够重用的自定义Web GUI组件。另外,Web组件开发商现在能提供更复杂的组件,并承诺Web应用程序开发人员能够轻松地使用这些组件。此类JSF用户界面组件必须集成并部署到JSF运行时框架中去,并在其中完全展开,还必须在设计时很好地集成到提供JSF支持的IDE中去。

  尽管JSF带来了标准用户界面框架,但对于开发第一个自定义JSF组件而言,还是存在几个缺陷和漏洞。让我们看看怎样创建一个纯HTML无法轻松创建的图形JSF组件。图形JSF组件的特点不仅要求生成DHTML,而且还需要对图像生成和客户端交互提供补充支持。我们将以一个图形组件的例子来阐述这些特点。该图形组件能够提供曲线图,并为各种客户端导航和交互提供便利。我们还会了解到将该图形组件集成到JSF-enabled IDE中所需要的步骤。通过理解图形组件的设计方法,您将会更好地理解如何实现JSF组件,而这应该能使您开发出定制的JSF图形组件。

什么是JSF?

  JSF是一种能够简化Web应用程序表示层结构的标准服务器端框架。定义JSF框架的JSR 127(参见参考资料)带有一个能提供基本UI组件(如输入栏和按纽)的参考实现。您可以将可重用用户界面组件集中起来创建Web页,将这些组件绑定到应用数据源上,并用服务器端事件控制程序处理客户端事件。根据说明书介绍,组件供应商能编写与JSF运行时框架集成的组件,并将其集成到在设计时与JSF兼容的IDE中去。

  从很大程度上讲,JSF组件同在HTML 2.0技术要求下可用的HTML组件和标签直接相符合。对许多Web应用程序而言,这套相对简单的组件是够用的。然而,许多应用程序如监管或监控程序需要更复杂的数据显示与交互,比如制表、制图和映射。由于JSF组件在HTML中直接提交复杂图形小部件的能力有限,所以设计这些高级组件的能力并不突出。解决方案要求服务器端组件向客户传输图像,却会给自身带来问题,因为在基本HTML图像上进行交互要受到限制。最后,使用JavaScript时,必须能调用客户端交互来使用户能对数据进行导航和交互。

  让我们看看开发一个简单的、将CSS输入HTML页面的JSF组件需要哪些步骤。当开发高级JSF图形组件时,这一简单组件的描述和代码样本会作为背景。图1显示了如何使用即将开发的组件,并显示将要得到的结果。使用这种组件的好处是能够通过改变某个JSF动作的组件值,来改变整个页面的外观。

图1:显示了我们如何使用一个非常简单的JSF组件将CSS输入某个HTML页面并得出结果。

开发组件

  JSF组件包含若干个Java类和配置文件。为创建一个自定义JSF组件,您需要开发一个扩展JSF基本组件类的Java类;为默认呈现软件包开发呈现程序;开发一个将在JSP页面中用于描述标签的Java类;编写一个标签库定义(TLD)文件;编写JSF配置文件。让我们更深入地了解这5个步骤。

  开发组件Java类。组件类负责管理代表组件状态的属性。因此,我们必须根据组件的行为(如输入组件或输出组件),给组件选择适当的基类(参见清单1)。这里描述的组件可进行扩展javax.faces.component.UIOutput,以显示指向某个样式表文件的URL,或内联式样式表的内容。该组件可用于在JSF动作中将某个样式表转换成另一个样式表。关联属性规定着值的类型:要么是一个URL,要么是内联样式。该组件还必须能够在向服务器发送请求期间,使用经过JSF框架处理的对象,来存储并修复自己的状态。组件的状态由重建对象所需的重要属性值组成。JSF框架自动调用saveState()和restoreState()方法,我们可以在组件中实现这两种方法来达到这一目标。

清单1. 组件类管理显示组件状态的属性。可依据组件的行为,为其选择一个适当的基类。在本例中,该组件扩展javax.faces.component.UIOutput,以显示指向某个样式表文件的URL,或者某个内联式样式表的内容。

import javax.faces.component.*;public class CSSComponent extends UIOutput {	private Boolean link; 	public String getFamily() { 		return "faces.CSSFamily"; 	} 	public boolean isLink() { 		if (link != null) 			return link.booleanValue(); 			ValueBinding vb = getValueBinding("link"); 	if (vb != null) { 		Boolean bvb = (Boolean) vb.getValue(			FacesContext.getCurrentInstance()); 		if (bvb != null) 			return bvb.booleanValue(); 	} 	return false; 	} 	public void setLink(boolean link) { 		this.link = new Boolean(link); 	} 	public Object saveState(FacesContext context) { 		return new Object[] { super.saveState(context), 			link }; 	} 	public void restoreState(FacesContext context, 	Object stateObj) { 		Object[] state = (Object[]) stateObj; 		super.restoreState(context, state[0]); 		link = (Boolean) state[1]; 	} }

开发呈现程序。呈现程序有两个作用。第一,呈现程序负责发送适当的HTML程序段,该程序段能在客户端中呈现组件。通常情况下,这个HTML程序段由一些适于呈现整个Web浏览器的HTML标签组成。此JSF生存周期称作编码阶段或呈现—响应阶段。该响应阶段还能发送增强客户端交互性的JavaScript代码。

  呈现程序的第二个作用是解析来自客户端的数据,从而对服务器端的组件状态进行更新(如用户在文本字段输入的文本)。标准呈现程序软件包具有强制性,但也可以提供其他呈现程序软件包,用于提供可替换的客户端表示法或SVG之类的语言(参见参考资料)。通过检验组件的连接属性,您实现的呈现程序(参见清单2)将选择在HTML页面中发送的CSS样式。

清单2. 标准呈现程序软件包具有强制性,但是,您可以使用其他呈现程序软件包,来提供可替换的客户端表示法或语言。通过检验组件的连接属性,您实现的呈现程序将选择在HTML页面中发出的CSS样式。

import javax.faces.component.UIComponent;import javax.faces.context.FacesContext;import javax.faces.context.ResponseWriter;import javax.faces.render.Renderer;public class CSSRenderer extends Renderer {public void encodeEnd(FacesContext context,UIComponent component)	throws IOException {		super.encodeEnd(context, component);		if (component instanceof CSSComponent) {			CSSComponent cssComponent  =				(CSSComponent) component;			String css = (String)cssComponent.getValue();			boolean isLink = cssComponent.isLink();			if (css != null) 				if (isLink)  				context.getResponseWriter().write(				"<link type=‘text/css‘ rel=‘stylesheet‘ 				href=‘" + css + "‘/>");			else 				context.getResponseWriter().write(					"<style>;\n" + css + "\n<style/>\n"); 		}	}}

开发标签类。同样,JSF框架提供了用于扩展的基类,来编写与组件相关的标签。该标签类负责定义并呈现将在faces-config.xml文件中应用的组件样式(这种样式的描述很简短)。它还负责创建JSF组件(由JSF框架来处理),传递JSF标签中所包含的属性,该属性用于初始化组件(参见清单3)。

清单3. 该标签类定义了将在faces-config.xml文件中应用的组件的样式和组件呈现方式。

import javax.faces.webapp.UIComponentTag;public class CSSTag 	extends UIComponentTag {	private String value;	private String link;	public String getComponentType() {		return "faces.CSSComponent";	}	public String getRendererType() {		return “HTML.LinkOrInlineRenderer";	}	protected void setProperties(UIComponent component) {		super.setProperties(component);		Application app = 			getFacesContext().getApplication();		if (value != null)			if (isValueReference(value)) 				component.setValueBinding("value", 				app.createValueBinding(value));			else				component.getAttributes().put("value", value);		if (link != null) 			if (isValueReference(link))				component.setValueBinding("link",				app.createValueBinding(link));			else				component.getAttributes().put("link",				new Boolean(link));	}	public String getLink() {		return link;	}	public void setLink(String link) {		this.link = link;	}	public String getValue() {		return value;	}	public void setValue(String value) {		this.value = value;	}}

  该标签提供setter和getter来管理链接和值属性。组件一旦创建,便会调用setProperties()方法,对标签属性进行初始化。每个标签属性都无外乎两种:要么是文字值,要么是bean属性的一个绑定。

编写标签库定义(TLD)。TLD是一个XML文件,它通过将标签名与相应的Java类相关联来描述标签。TLD还描述了标签所允许的属性(参见清单4)。这个TLD定义了一个名为css的标签,该标签绑定到CSSTag类。它还声明了链接和值标签属性。

清单4. TLD是一个通过将标签名与相应的Java类相关联来描述标签的XML文件。TLD定义了名为css的标签,使其与CSSTag类绑定。它还声明了链接和值标签属性。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE taglib PUBLIC 	"-//Sun Microsystems, Inc.//	DTD JSP Tag Library  1.2//EN" 	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"><taglib>	<tlib-version>1.0</tlib-version>	<jsp-version>1.2</jsp-version>	<short-name>custom</short-name>	<uri>http://www.ilog.com/jviews/tlds/css.tld</uri>	<description>This tag library contains a tag for a 		sample custom JSF Component.</description>	<tag>		<name>css</name>		<tag-class>path.to.CSSTag</tag-class>		<description>A component that displays the style 			inline or a link a to a css file</description>		<attribute>			<name>id</name>			<required>false</required>			<rtexprvalue>false</rtexprvalue>			<type>java.lang.String</type>			<description>The id of this component.			</description>		</attribute>		<attribute>			<name>binding</name>			<required>false</required>			<rtexprvalue>false</rtexprvalue>			<type>java.lang.String</type>			<description>The value binding expression 				linking this component to a property in a				backing bean. If this attribute is set, the 				tag does not create the component itself but				retrieves it from the bean property. This				attribute must be a value binding.			</description>		</attribute>		<attribute>			<name>value</name>			<required>true</required>			<rtexprvalue>false</rtexprvalue>			<type>java.lang.String</type>			<description>The inline css text or the url to 				the css file to link.</description>		</attribute>		<attribute>			<name>link</name>			<required>false</required>			<rtexprvalue>false</rtexprvalue>			<type>java.lang.String</type>			<description>Whether the value is a link or 				the inline style.</description>		</attribute>	</tag></taglib>

编写JSF配置文件。为了将某个JSF组件集成到框架中,您必须提供一个名为faces-config.xml的配置文件。该文件将组件类型和呈现程序类型(用于JSP定制标签处理程序)与对应的Java类关联起来。它还能描述与每个组件一同使用的呈现程序(参见清单5)。该文件定义了faces.CSSFamily组件家族。在本例中,该家族由faces.CSSComponent这一个组件类型(该类型与CSSComponent类绑定)组成。最后,HTML.LinkOrInlineRenderer类型的呈现程序(由CSSComponent类实现)要与faces.CSSFamily家族相关联。

清单5. 该文件将组件类型和呈现程序类型与对应的Java类联系起来,并描述与每个组件一同使用的呈现程序。它还定义了faces.CSSFamily组件家族。
<!DOCTYPE faces-config PUBLIC 	"-//Sun Microsystems, Inc.//	DTD JavaServer Faces Config 1.0//EN" 	"http://java.sun.com/dtd/web-facesconfig_1_0.dtd"><faces-config>	<component>		<component-type>faces.CSSComponent		</component-type>		<component-class>path.to.CSSComponent		</component-class>		<component-extension>			<component-family>faces.CSSFamily			</component-family>			<renderer-type>HTML.LinkOrInlineRenderer			</renderer-type>		</component-extension>	</component>	<render-kit>		<renderer>			<component-family>faces.CSSFamily			</component-family>			<renderer-type> HTML.LinkOrInlineRenderer 			</renderer-type>			<renderer-class>path.to.CSSRenderer			</renderer-class>		</renderer>	/render-kit></faces-config>

开始制图

  如果您希望将自己的组件集成到JSF-enabled IDE中,您还可以提供补充说明。比如说,除提供其他的设计时信息外,还可以提供一个名为sun-faces-config.xml的XML配置文件,用于描述应在IDE中公开的组件属性。

  既然已经看到如何创建一个简单的JSF组件,不妨再来看看怎样创建一个图形JSF组件。我们将遵循同样的基本步骤来设计一个高级JSF图形组件。让我们以一个图形组件(如ILOG JSF图形组件)为例,通过一组分类,该组件为数据值分布提供了可视化表示。该图形能够以条型统计图、圆形分格统计图和气泡式统计图等各种显示方法来显示数据集合。该JSF图形组件有两个初始设计限制:

  我们已经拥有Java图形bean组件,它具备所有图形显示能力。该组件可以显示很多图形,而且可定制性很高。在理想情况下,我们希望利用bean组件,使用它的功能来构成我们的JSF组件的基础。

  普通JSF应用程序需要重新载入整个页面以更新视图。这种方法适合基于表单的应用程序,但在很多情况下却不适用于高度图形化的用户界面。因此,我们的JSF图形组件必须能在不更新整个页面的前提下处理某些简单的导航,以提供更好的用户体验。

  以下是满足这些需求的解决方案:该JSF图形组件将管理图形bean组件,包括创建图形bean、定制该bean以及使该bean可用于服务器端操作。呈现JSF组件将分为两个阶段完成。JSF呈现程序会产生一个<img>标签和一套JavaScript对象(参见图2)。客户端将请求服务器发回一张图像。这一请求由某个servlet完成,该servlet获得图形bean,并利用图形提供的方法生成一幅图像(参见图3)。任何只改变该图形外观的进一步用户交互(放大、扫视、更改样式表等)都会引起图形的一次增量更新。如果客户端不只是要求对图形图像进行更新,那么将提交该页面(参见图4)。

  图2JSF图形组件管理图形bean组件,包括创建图形bean、对其进行定制,并使其可用于服务器端动作。JSF呈现程序生成一个<img>标签和一套JavaScript对象。

  图3 客户机通过servlet要求服务器获得一张图像。该servlet获得图形bean,并通过由图形提供的方法生成一幅图像。

  图4如果客户端不只是要求对图形外观的进行更新,那么页面将被提交。

  JSF图形组件还配有一套附加的JSF组件。overview可显示该图形整体视图,显示一个代表图形视图的长方形,还应允许用户扫描可视区域。legend组件可显示数据集合的相关信息,还能自行在图形中显示,依被显示数据的样式而定。也能提供客户端的interactors如扫描和放大,这些功能可看成是客户端交互,表示与图形的交互不会像一次正常的JSF交互那样重新载入整个页面。

  要想呈现图形组件,只需使用chartView标签:

<jvcf:chartView id="c" style="width:500px;height:300px" … />

  该数据在HTML页面中作为图像显示。该图像由servlet创建,旨在响应一次HTTP请求(该请求包括指定结果图像、生成图像映射以及生成内联式图例等各种参数)。结果图像随之被嵌入客户端DOM,页面中只有图像自身这一部分被更新。

应用程序核心部件

  让我们看看简单的定制JSF组件和高级图形组件之间的一些区别。JSF图形组件类很像一个标准组件,不过是多了一个可访问图形bean(该图形bean负责生成在HTML页面中显示的图像)的图形属性。JSF组件可以通过某个绑定值或在当前会话中对这个图形bean进行局部检索。当JSF图形组件成为某个应用程序的核心部件时,可选的JSF组件(如概览或图例)便与主图形相关联,来显示附加信息(见清单6)。

清单6. 当JSF图形组件成为某个应用程序的核心部件时,可选的JSF组件便与主图形相关联,来显示附加信息。

<jvcf:chartZoomInteractor id="chartZoomInteractor" 	XZoomAllowed="true" 	YZoomAllowed="true" /> <jvcf:chartView id="chartView" 						 chart="#{myBean.chart}" 					 servlet="demo.ImageMapServlet" 			interactorId="chartZoomInteractor" 						 width="500" 						height="300" 			 styleSheets="/data/line.css" 			waitingImage="data/images/wait.gif" 			 imageFormat="PNG"  /><jvcf:chartOverview id="chartOverview" 					style="height:100;width:150px" 						viewId="chartView" 						lineWidth="3" 						lineColor="red" /><jvcf:chartLegend id="legendView" 							viewId="chartView" 							 width="400" 							height="180" 							layout="vertical" 				waitingImage="data/images/wait.gif" />

  呈现程序是实现这个JSF的一大难点。如前所述,呈现程序并不生成简单的HTML,而是生成由HTML(<IMG> tag)和JavaScript proxy(代理程序)组成的动态HTML(DHTML)。

  Proxy是一个负责管理客户机组件图像显示的JavaScript类实例。该对象是服务器端Java组件类在客户端显示;它与组件类具有相同属性。页面上的每个组件、图形及其配件都有一个proxy实例。呈现JavaScript时,在每个可视的JavaScript变量上使用facesContext.getExternalContext().encodeNamespace(name)方法是个很好的实践。这样做在今后方便地将组件集成到到JSR 168-compliant端口环境中。

  为举例说明客户机上的proxy,必须在页面上导入JavaScript支持库。为保持客户端尽量瘦,需要基于JavaScript库支持的proxy类,对JavaScript库进行模块化。因此,需要给每个proxy类输入一套不同的、有可能重叠的库。图形呈现的困难部分,出现在发送这些script库的阶段。每个组件的呈现程序都要声明自己需要哪个库,以及什么时候发送引用的库,必须认清已发送的库,以避免重复。仅在页面呈现期间的存在script管理器负责这项筛选工作。每当呈现程序想要发送整套库输入时,它都会向筛选出已发送库的script管理器提供列表。

  客户端proxy的目的在于允许编写脚本,并避免不必要的页面更新。一旦呈现了图形,在客户端便可使用proxy,以便动态安装interactor,并显示或隐藏图像映射。Proxy对象也可供支持JavaScript鼠标事件处理的常规JSF组件使用。

<jvcf:chartView id=	"chartView" .. /><h:selectBooleanCheckbox id=	"genImageMap" onclick=	"chartView.setGenerateImageMap(	this.checked ? true : false,  true);" />

  对组件客户端proxy进行局部修改的问题在于,其状态不再与服务器上的Java组件的状态同步。为解决这个问题,proxy使用一个隐藏的输入标签(<INPUT TYPE="HIDDEN">)来保存客户机上的新状态。当执行某个标准JSF动作并提交页面时,呈现程序将解析该隐藏状态,使客户机与服务器同步。这种行为需要呈现程序类中有专门的破解行为。标准破解方法得以改进,以便解析来自客户机的状态,并更新服务器端组件的状态。

测试实例

  图形及其相关组件之间的关联由标记引用与绑定来完成。为使页面设计具有灵活性,一个组件可以在呈现之前被引用。因此,在呈现时间内,如果某个组件属性引用另一个尚未呈现的组件,那么,将延迟发送依赖于客户机进行解决的JavaScript代码,直到呈现已引用的组件。此工作可由依赖性管理器完成。

为证实这一点,不妨看一个典型实例,该实例涉及某个概览,该概览引用一张图形。

<jvcf:overview viewId=	"chart" [...] /> <jvcf:chartView id=	"chart" [....] />

  存在两种情况。被引用图形组件已经呈现,因此不存在任何问题

JSP:<jvcf:chartView id=	"chart" [....] /><jvcf:overview viewId=	"chart" id="overview" [...] /> render:[...]var chart = 	new IlvChartViewProxy ( .. );[...]var overview= 	new IlvFacesOverviewProxy (	 .. );overview.setView(chart);[...]

  已引用图形的组件在依赖的概览组件之前不会呈现。既然如此,可在依赖性管理器上注册一个组件创建监视器。已引用图形组件最终呈现时,其呈现程序会通知自己创建的依赖性管理器。此时,将发送解决依赖性所需的代码:

JSP:<jvf:overview viewId=	"chart" id="overview" [...] /> <jvdf:chartView id=	"chart" [....] />render:[...]var overview = 	new IlvFacesOverviewProxy (	 .. );[...]var chart = 	new IlvChartViewProxy ( .. );overview.setView(chart);[...]

  开发JSF组件的目的之一,是能够将它们应用于任何与JSF兼容的IDE。尽管如此,JSF兼容性并不足以保证这种设计时集成将会有效。下面是在开发JSF组件过程中,为了便于在今后与IDE集成需要注意的一些简单思想:

  首先,定制JSF组件应该提供一个基本HTML呈现程序。在设计时,JSF IDE不能呈现请求有效数据或app服务器连接的动态图形组件。因此,带有复杂的或非传统的(比如不是HTML)呈现程序的组件,应该使用Beans.isDesignTime()来确定是提供一个基本HTML表示法,还是提供真正的组件呈现程序。

  另一个设计时问题是组件的位置和大小。不同IDE使用不同的标志和属性。能够调整大小的组件(如一幅图像)应能处理定义大小的不同方式。

  最后,为了与IDE集成,组件必须提供尚未被JSF说明定义的补充信息。遗憾的是,当前每个IDE都需要特殊处理程序来集成组件,即:在一种情况就需要XML文件,而在另一种情况下需要eclipse插件,如此等等。下一个JSF JSR(2.0版)的主要目的之一,将是指定附加的元数据格式。

  如您所见,编写一个简单的JSF组件并不难,因为框架已经完成了大部分工作。JSF框架管理着组件状态、呈现程序等等。在本文中,我们已经扩展了这些基本概念,来设计一个能够显示复杂元数据、提供增量更新、支持大量客户端交互并与配套组件协作的高级图形JSF组件。支持这些特点需要对基本JSF组件的结构进行许多改进。当然,增量更新的概念今后对JSF框架将是一个很好的完善,因为它只允许呈现页面已改变的部分,避免了更新整个页面。按照JSF说明书工作往往不足以确保组件完全集成到JSF IDE中;一个新JSR应能及时解决这些难题。尽管存在缺陷,JSF框架仍能极大地加快Web组件开发速度、方便的融合来自各种资源的组件,以创建完整的、复杂的Web应用程序。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
JSF,早晨八九点钟的太阳冉冉升起
集成JSP/JSF和XML/XSLT
JSP和JSF合并 共同打造完美的Web应用
联合使用 CSS、JavaScript 和 JSF 精心打造 Ajax 应用程序,第 1 部分: 增强 JSF 页面的外观
Matrix - 与 Java 共舞 - [翻译]JSF(JavaServer Faces) 介绍(jsp/servlet 技术)
NetBeans或吞并Creator 同室操戈为哪般?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服