如今Q许多Web应用E序开发h员都在{而用JSFQ但是他们发玎ͼ预先定制的JSF UIlg受到基本DHTMLH口部g的限制。监或业务程监控之类的高U应用程序需要能与JSF框架兼容的高U可视化lg。JSF框架的标准化使它易于开发能够重用的自定义Web GUIlg。另外,Weblg开发商现在能提供更复杂的组Ӟq承诺Web应用E序开发h员能够轻村֜使用q些lg。此cJSF用户界面lg必须集成q|到JSFq行时框架中去,q在其中完全展开Q还必须在设计时很好地集成到提供JSF支持的IDE中去?/P>
管JSF带来了标准用L面框Ӟ但对于开发第一个自定义JSFlg而言Q还是存在几个缺陷和漏洞。让我们看看怎样创徏一个纯HTML无法L创徏的图形JSFlg。图形JSFlg的特点不仅要求生成DHTMLQ而且q需要对囑փ生成和客L交互提供补充支持。我们将以一个图形组件的例子来阐q这些特炏V该囑Şlg能够提供曲线图,qؓ各种客户端导航和交互提供便利。我们还会了解到该囑Şlg集成到JSF-enabled IDE中所需要的步骤。通过理解囑Şlg的设计方法,您将会更好地理解如何实现JSFlgQ而这应该能您开发出定制的JSF囑Şlg?/P>
什么是JSFQ?/STRONG>
JSF是一U能够简化Web应用E序表示层结构的标准服务器端框架。定义JSF框架的JSR 127Q参见参考资料)带有一个能提供基本UIlgQ如输入栏和按纽Q的参考实现。您可以可重用用户界面lg集中h创徏Web,这些组件绑定到应用数据源上Qƈ用服务器端事件控制程序处理客L事g。根据说明书介绍Q组件供应商能编写与JSFq行时框枉成的lgQƈ其集成到在设计时与JSF兼容的IDE中去?/P>
从很大程度上ԌJSFlg同在HTML 2.0技术要求下可用的HTMLlg和标{接相W合。对许多Web应用E序而言Q这套相对简单的lg是够用的。然而,许多应用E序如监或监控E序需要更复杂的数据显CZ交互Q比如制表、制囑֒映射。由于JSFlg在HTML中直接提交复杂图形小部g的能力有限,所以设计这些高U组件的能力q不H出。解x案要求服务器端组件向客户传输囑փQ却会给自n带来问题Q因为在基本HTML囑փ上进行交互要受到限制。最后,使用JavaScriptӞ必须能调用客L交互来用户能对数据q行D和交互?/P>
让我们看看开发一个简单的、将CSS输入HTML面的JSFlg需要哪些步骤。当开发高UJSF囑ŞlgӞq一单组件的描述和代码样本会作ؓ背景。图1昄了如何用即开发的lgQƈ昄要得到的结果。用这U组件的好处是能够通过改变某个JSF动作的组件|来改变整个页面的外观?
?Q显CZ我们如何使用一个非常简单的JSFlgCSS输入某个HTML面q得出结果?/P>
开发组?/STRONG>
JSFlg包含若干个Javacd配置文g。ؓ创徏一个自定义JSFlgQ您需要开发一个扩展JSF基本lgcȝJavac;为默认呈现Y件包开发呈现程序;开发一个将在JSP面中用于描q标{Javac;~写一个标{ֺ定义QTLDQ文Ӟ~写JSF配置文g。让我们更深入地了解q?个步骤?/P>
开发组件JavacR组件类负责理代表lg状态的属性。因此,我们必须Ҏlg的行为(如输入组件或输出lgQ,l组仉择适当的基c(参见清单1Q。这里描q的lg可进行扩展javax.faces.component.UIOutputQ以昄指向某个样式表文件的URLQ或内联式样式表的内宏V该lg可用于在JSF动作中将某个样式表{换成另一个样式表。关联属性规定着值的cdQ要么是一个URLQ要么是内联样式。该lgq必能够在向服务器发送请求期_使用l过JSF框架处理的对象,来存储ƈ修复自己的状态。组件的状态由重徏对象所需的重要属性值组成。JSF框架自动调用saveState()和restoreState()ҎQ我们可以在lg中实现这两种Ҏ来达到这一目标?/P>
清单1. lgcȝ理显C组件状态的属性。可依据lg的行为,为其选择一个适当的基cR在本例中,该组件扩展javax.faces.component.UIOutputQ以昄指向某个样式表文件的URLQ或者某个内联式样式表的内容?STRONG>
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]; } }
开发呈现程序?/STRONG>呈现E序有两个作用。第一Q呈现程序负责发送适当的HTMLE序D,该程序段能在客户端中呈现lg。通常情况下,q个HTMLE序D는一些适于呈现整个Web览器的HTML标签l成。此JSF生存周期UC~码阶段或呈现—响应阶Dc该响应阶段q能发送增强客L交互性的JavaScript代码?/P>
呈现E序的第二个作用是解析来自客L的数据,从而对服务器端的组件状态进行更斎ͼ如用户在文本字段输入的文本)。标准呈现程序Y件包h强制性,但也可以提供其他呈现E序软g包,用于提供可替换的客户端表C法或SVG之类的语aQ参见参考资料)。通过验组件的q接属性,您实现的呈现E序Q参见清?Q将选择在HTML面中发送的CSS样式?/P>
清单2. 标准呈现E序软g包具有强制性,但是Q您可以使用其他呈现E序软g包,来提供可替换的客L表示法或语言。通过验组件的q接属性,您实现的呈现E序选择在HTML面中发出的CSS样式?/P> 开发标{?/STRONG>同样QJSF框架提供了用于扩展的基类Q来~写与组件相关的标签。该标签c负责定义ƈ呈现在faces-config.xml文g中应用的lg样式Q这U样式的描述很简短)。它q负责创建JSFlgQ由JSF框架来处理)Q传递JSF标签中所包含的属性,该属性用于初始化lgQ参见清?Q?/P>
清单3. 该标{定义了将在faces-config.xml文g中应用的lg的样式和lg呈现方式?/P> 该标{提供setter和getter来管理链接和值属性。组件一旦创建,便会调用setProperties()ҎQ对标签属性进行初始化。每个标{ֱ性都无外乎两U:要么是文字|要么是bean属性的一个绑定?/P>
~写标签库定义(TLDQ?/STRONG>TLD是一个XML文gQ它通过标{与相应的Javacȝ兌来描q标{。TLDq描qC标签所允许的属性(参见清单4Q。这个TLD定义了一个名为css的标{,该标{定到CSSTagcR它q声明了链接和值标{ֱ性?/P>
清单4. TLD是一个通过标{与相应的Javacȝ兌来描q标{XML文g。TLD定义了名为css的标{,使其与CSSTagcȝ定。它q声明了链接和值标{ֱ性?/P> ~写JSF配置文g?/STRONG>Z某个JSFlg集成到框架中Q您必须提供一个名为faces-config.xml的配|文件。该文g组件类型和呈现E序cdQ用于JSP定制标签处理E序Q与对应的Javacd联v来。它q能描述与每个组件一同用的呈现E序Q参见清?Q。该文g定义了faces.CSSFamilylg家族。在本例中,该家族由faces.CSSComponentq一个组件类型(该类型与CSSComponentcȝ定)l成。最后,HTML.LinkOrInlineRenderercd的呈现程序(由CSSComponentcd玎ͼ要与faces.CSSFamily家族相关联?/P>清单5. 该文件将lgcd和呈现程序类型与对应的Javac联pv来,q描qC每个lg一同用的呈现E序。它q定义了faces.CSSFamilylg家族? 开始制? 如果您希望将自己的组仉成到JSF-enabled IDE中,您还可以提供补充说明。比如说Q除提供其他的设计时信息外,q可以提供一个名为sun-faces-config.xml的XML配置文gQ用于描q应在IDE中公开的组件属性? 既然已经看到如何创徏一个简单的JSFlgQ不妨再来看看怎样创徏一个图形JSFlg。我们将遵@同样的基本步骤来设计一个高UJSF囑Şlg。让我们以一个图形组Ӟ如ILOG JSF囑ŞlgQؓ例,通过一l分c,该组件ؓ数据值分布提供了可视化表C。该囑Ş能够以条型统计图、圆形分格统计图和气泡式l计囄各种昄Ҏ来显C数据集合。该JSF囑Şlg有两个初始设计限Ӟ 我们已经拥有Java囑ŞbeanlgQ它具备所有图形显C力。该lg可以昄很多囑ŞQ而且可定制性很高。在理想情况下,我们希望利用beanlgQ用它的功能来构成我们的JSFlg的基? 普通JSF应用E序需要重新蝲入整个页面以更新视图。这U方法适合Z表单的应用程序,但在很多情况下却不适用于高度图形化的用L面。因此,我们的JSF囑Şlg必须能在不更新整个页面的前提下处理某些简单的DQ以提供更好的用户体验? 以下是满些需求的解决ҎQ该JSF囑Şlg管理图形beanlgQ包括创建图形bean、定制该bean以及使该bean可用于服务器端操作。呈现JSFlg分Z个阶D完成。JSF呈现E序会生一?lt;img>标签和一套JavaScript对象Q参见图2Q。客L请求服务器发回一张图像。这一h由某个servlet完成Q该servlet获得囑ŞbeanQƈ利用囑Ş提供的方法生成一q图像(参见?Q。Q何只改变该图形外观的q一步用户交互(攑֤、扫视、更Ҏ式表{)都会引v囑Ş的一ơ增量更新。如果客L不只是要求对囑Ş囑փq行更新Q那么将提交该页面(参见?Q?/P>
?JSF囑Şlg理囑ŞbeanlgQ包括创建图形bean、对其进行定Ӟq其可用于服务器端动作。JSF呈现E序生成一?lt;img>标签和一套JavaScript对象?/P>
? 客户机通过servlet要求服务器获得一张图像。该servlet获得囑ŞbeanQƈ通过由图形提供的Ҏ生成一q图像? ?如果客户端不只是要求对图形外观的q行更新Q那么页面将被提交?/P>
JSF囑Şlgq配有一套附加的JSFlg?EM>overview可显C囑Ş整体视图Q显CZ个代表图形视囄长方形,q应允许用户扫描可视区域?EM>legendlg可显C数据集合的相关信息Q还能自行在囑Ş中显C,依被昄数据的样式而定。也能提供客L?EM>interactors如扫描和攑֤Q这些功能可看成是客L交互Q表CZ囑Ş的交互不会像一ơ正常的JSF交互那样重新载入整个面?/P>
要想呈现囑ŞlgQ只需使用chartView标签Q? <jvcf:chartView id="c" style="width:500px;height:300px" ?/> 该数据在HTML面中作为图像显C。该囑փ由servlet创徏Q旨在响应一ơHTTPhQ该h包括指定l果囑փ、生成图像映以及生成内联式图例{各U参敎ͼ。结果图像随之被嵌入客户端DOMQ页面中只有囑փ自nq一部分被更新?/P>
应用E序核心部g 让我们看看简单的定制JSFlg和高U图形组件之间的一些区别。JSF囑Şlgcd像一个标准组Ӟ不过是多了一个可讉K囑ŞbeanQ该囑Şbean负责生成在HTML面中显C的囑փQ的囑Ş属性。JSFlg可以通过某个l定值或在当前会话中对这个图形beanq行局部检索。当JSF囑Şlg成ؓ某个应用E序的核心部件时Q可选的JSFlgQ如概览或图例)便与d形相兌Q来昄附加信息Q见清单6Q?/P>
清单6. 当JSF囑Şlg成ؓ某个应用E序的核心部件时Q可选的JSFlg便与d形相兌Q来昄附加信息?/P> 呈现E序是实现这个JSF的一大难炏V如前所qͼ呈现E序q不生成单的HTMLQ而是生成由HTMLQ?lt;IMG> tagQ和JavaScript proxyQ代理程序)l成的动态HTMLQDHTMLQ?/P>
Proxy是一个负责管理客hlg囑փ昄的JavaScriptcd例。该对象是服务器端Javalgcd客户端显C;它与lgcd有相同属性。页面上的每个组件、图形及光仉有一个proxy实例。呈现JavaScriptӞ在每个可视的JavaScript变量上用facesContext.getExternalContext().encodeNamespace(name)Ҏ是个很好的实c这样做在今后方便地组仉成到到JSR 168-compliant端口环境中?/P>
ZD例说明客h上的proxyQ必d面上导入JavaScript支持库。ؓ保持客户端尽量瘦Q需要基于JavaScript库支持的proxyc,对JavaScript库进行模块化。因此,需要给每个proxyc输入一套不同的、有可能重叠的库。图形呈现的困难部分Q出现在发送这些script库的阶段。每个组件的呈现E序都要声明自己需要哪个库Q以及什么时候发送引用的库,必须认清已发送的库,以避免重复。仅在页面呈现期间的存在script理器负责这筛选工作。每当呈现程序想要发送整套库输入Ӟ它都会向{选出已发送库的script理器提供列表?/P>
客户端proxy的目的在于允许编写脚本,q免不必要的页面更新。一旦呈C囑ŞQ在客户端便可用proxyQ以便动态安装interactorQƈ昄或隐藏图像映。Proxy对象也可供支持JavaScript鼠标事g处理的常规JSFlg使用? 对组件客Lproxyq行局部修改的问题在于Q其状态不再与服务器上的Javalg的状态同步。ؓ解决q个问题Qproxy使用一个隐藏的输入标签Q?lt;INPUT TYPE="HIDDEN">Q来保存客户Z的新状态。当执行某个标准JSF动作q提交页面时Q呈现程序将解析该隐藏状态,使客h与服务器同步。这U行为需要呈现程序类中有专门的破解行为。标准破解方法得以改q,以便解析来自客户机的状态,q更新服务器端组件的状态?/P>
试实例 囑Ş及其相关lg之间的关联由标记引用与绑定来完成。ؓ佉K面设计具有灵zL,一个组件可以在呈现之前被引用。因此,在呈现时间内Q如果某个组件属性引用另一个尚未呈现的lgQ那么,gq发送依赖于客户行解决的JavaScript代码Q直到呈现已引用的组件。此工作可由依赖性管理器完成?/P>
实这一点,不妨看一个典型实例,该实例涉及某个概览,该概览引用一张图形?/P> 存在两种情况。被引用囑Şlg已经呈现Q因此不存在M问题 已引用图形的lg在依赖的概览lg之前不会呈现。既然如此,可在依赖性管理器上注册一个组件创建监视器。已引用囑Şlg最l呈现时Q其呈现E序会通知自己创徏的依赖性管理器。此Ӟ发送解决依赖性所需的代码: 开发JSFlg的目的之一Q是能够它们应用于M与JSF兼容的IDE。尽如此,JSF兼容性ƈ不以保证这U设计时集成会有效。下面是在开发JSFlgq程中,Z便于在今后与IDE集成需要注意的一些简单思想Q?/P>
首先Q定制JSFlg应该提供一个基本HTML呈现E序。在设计ӞJSF IDE不能呈现h有效数据或app服务器连接的动态图形组件。因此,带有复杂的或非传l的Q比如不是HTMLQ呈现程序的lgQ应该用Beans.isDesignTime()来确定是提供一个基本HTML表示法,q是提供真正的组件呈现程序?/P>
另一个设计时问题是组件的位置和大。不同IDE使用不同的标志和属性。能够调整大的lgQ如一q图像)应能处理定义大小的不同方式?/P>
最后,Z与IDE集成Q组件必L供尚未被JSF说明定义的补充信息。遗憄是,当前每个IDE都需要特D处理程序来集成lgQ即Q在一U情况就需要XML文gQ而在另一U情况下需要eclipse插gQ如此等{。下一个JSF JSRQ?.0版)的主要目的之一Q将是指定附加的元数据格式?/P>
如您所见,~写一个简单的JSFlgq不难,因ؓ框架已经完成了大部分工作。JSF框架理着lg状态、呈现程序等{。在本文中,我们已经扩展了这些基本概念,来设计一个能够显C复杂元数据、提供增量更新、支持大量客L交互q与配套lg协作的高U图形JSFlg。支持这些特炚w要对基本JSFlg的结构进行许多改q。当Ӟ增量更新的概念今后对JSF框架是一个很好的完善Q因为它只允许呈现页面已改变的部分,避免了更新整个页面。按照JSF说明书工作往往不以确保组件完全集成到JSF IDE中;一个新JSR应能及时解决q些N。尽存在缺PJSF框架仍能极大地加快Weblg开发速度、方便的融合来自各种资源的组Ӟ以创建完整的、复杂的Web应用E序?/P>
参考资?/STRONG> 作者简?/STRONG> Marc Durocher是ILOG的一名Y件架构师QILOG是企业软glg和服务的主要提供商。Marc Durocher在ILOG负责开发ILOG JViews生U上的JSFlg。可以通过mdurocher@ilog.fr联系Marc?/P>
原文出处 http://www.ftponline.com/weblogicpro/2005_03/magazine/features/mdurocher/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");
}
}
}
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;
}
}
<?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>
<!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>
<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" />
<jvcf:chartView id=
"chartView" .. />
<h:selectBooleanCheckbox id=
"genImageMap" onclick=
"chartView.setGenerateImageMap(
this.checked ? true : false,
true);" />
<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);
[...]
Java 工具
Sun Java Studio Creator
Developing Web Applications with JavaServer Faces
Rational 软g
Rational Application Developer for WebSphere Software
Java Specification Requests JSR 127: JavaServer Faces
JSR 168: Portlet Specification
]]>
The Java 2 Enterprise Edition includes JavaServer Faces technology, which provides a mature and extensible user interface component model. The design of this model makes it easy for application developers to create custom components by extending the standard components included with JavaServer Faces technology and to reuse these components across applications.
The example featured in this entry includes a JavaServer Faces text field component that takes the name of a city. When the user begins entering characters in the field, the application uses AJAX to perform auto completion on the data by matching the user's input to a list of cities stored on the server. In two of the versions of this example, the text field component is a custom JavaServer Faces component that provides the AJAX support. This component shields the page author from the complexities of AJAX by rendering all the HTML and JavaScript code that is required to incorporate AJAX capabilities into an application.
A third version of this example adds AJAX support to a standard JavaServer Faces component through a servlet that interacts with the JavaServer Faces component and other JavaServer Faces server-side objects to generate the appropriate auto completion data.
This entry focuses primarily on the question, "How can I incorporate AJAX functionality into a JavaServer Faces application?"
Among the other questions that this entry considers are the following:
Developers who want to include AJAX support in JavaServer Faces applications have more than one strategy to choose from. Which strategy they choose depends on the needs of their situation. Developers might ask themselves the following questions to help them decide which strategy to select:
This entry discusses three strategies for incorporating AJAX support into a JavaServer Faces application. The first two strategies involve creating a custom component to generate the necessary JavaScript and to make AJAX interactions with another server-side component. In Strategy 1, the JavaServer Faces technology life cycle and the custom component handle the AJAX requests. In Strategy 2, the custom component communicates with a special servlet, which handles all the AJAX requests. Strategy 3 does not require any custom components; all the additional AJAX support is performed by a servlet provided by the developer.
Strategy 1 and Strategy 2 require knowledge of the JavaServer Faces technolgy life cycle to implement them. Strategy 3 requires knowledge of the Java servlet API.
The following sections explain the three strategies and provide more detail on their advantages and disadvantages. After reading about these strategies and answering the list of questions above, developers should be able to select the strategy that is appropriate for their particular situation. The Comparison of Strategies section includes a chart that summarizes the characteristics of each strategy, which makes it even easier to make the decision.
In this strategy the JavaServer Faces component does three things:
Figure 1: Architecture of a JavaServer Faces Component that Renders Client-Side AJAX JavaScript and Processes AJAX Requests
The following steps explain the architecture illustrated in Figure 1:
script
element rendered by the renderer, AutoCompleteTextRenderer
.
faces/autocomplete-script
, which is mapped to a FacesServlet
instance. This instance processes the RenderPhaseListener
instance, which recognizes the URI and returns the component.js
page containing the client-side JavaScript code necessary for the AJAX interactions. After the component.js
page is returned, the RenderPhaseListener
instance stops taking part in the JavaServer Faces technology life cycle processing for now.
onkeypress
event occurs. The JavaScript function mapped to this event creates an XMLHttpRequest
object and configures it with the URL to the FacesServlet
instance. This URL is faces/autocomplete&id=San
. The id=San
part of the URL is a URL parameter in which San
is the user input that is matched against the list of cities. The XMLHttpRequest
object makes a call to the FacesServlet
instance. HTML page events continue to be processed by the JSP page. FacesServlet
instance processes the RenderPhaseListener
instance that recognizes the URL faces/autocomplete
and looks up the AutoCompleteTextField
component, which provides the completion logic.
RenderPhaseListener
instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest
object, which then calls the XMLHttpRequest
callback function.
XMLHttpRequest
callback function updates the HTML DOM based on the contents of the XML document that was returned.
FacesServlet
instance, which updates SessionBean
with the address information entered by the user.The remainder of this section provides further details on how this strategy works.
In Figure 1, the page called Enter Address Page is a JavaServer Faces page. This page contains a tag that represents the AutocompleteTextField
component, which is a JavaServer Faces component. This component renders two things: an HTML text field, into which the user enters the name of a city, and the necessary client-side JavaScript AJAX code that fetches possible city names from a server-side managed bean. This managed bean contains a method that implements the algorithm for completing city names.
To add the AJAX-enabled component to the application, the page author need only include the following tag in the page:
<ajaxTags:completionField size="40" id="cityField"
completionMethod="#{AutoCompleteTextfield.completeCity}"
value="#{SessionBean.city}" required="true"
/>
Code Example 1: Declaring an AJAX-enabled JavaServer Faces Component in a JavaServer Faces page
TheAutoCompleteTextFieldTag
tag handler implements the ajaxTags:completionField
tag, which is rendered to HTML by the renderer, AutoCompleteTextFieldRenderer
. The first time the component is encountered in the page, AutoCompleteTextFieldRenderer
renders a reference to external JavaScript code, as shown in this HTML fragment:
<script type="text/javascript" src="faces/autocomplete-script">
Code Example 2: Rendered HTML that references JavaScript to be returned by RenderPhaseListener
When the reference to the external script file is reached, the browser makes a request to the URL, faces/autocomplete-script
. The request first goes to the FacesServlet
instance and is then received by the RenderPhaseListener
instance, which returns the client-side JavaScript relevant to the AJAX interactions of the component. In the case of this example, the component.js
file is included with the component and returned using the class loader. Bundling the relevant JavaScript code with the component is a good practice because it keeps the script and component close to each other but keeps the JavaScript out of the component code. The AJAX client-side JavaScript contains the XMLHttpRequest
callback method, which includes the event mappings to the form elements rendered by AutoCompleteTextFieldRenderer
.
When the target onkeypress
event is encountered in the City form field, the client-side XMLHttpRequest
object makes a HTTP GET call with the partial text input from the field to the FacesServlet
instance, which in turn calls the RenderPhaseListener
instance. The RenderPhaseListener
instance looks up the AutoCompleteTextField
component and gets a set of city names that match partial text input. For example, if the user entered "San" into the field, the component would fetch the set of city names that start with the string "San".
At this point, the RenderPhaseListener
instance can access the view state associated with the page if it is needed to process the interaction. When processing AJAX requests with JavaServer Faces components, a developer can treat each AJAX request as a new request by using simple HTTP GET calls. In this case, every request is a new Faces request. Or the developer can use POST calls, which give access to the view state associated with the JavaServer Faces page. Strategy 1A provides further details on using POST calls to access view state. When using AJAX GET requests with JavaServer Faces technology, a developer has access to managed bean components and has the ability to evaluate expressions but does not have access to the view state.
In the case of this example, each auto complete AJAX request is a GET request and is therefore a new Faces request. The RenderPhaseListener
instance renders an XML response based on the results obtained from the AutoCompleteTextField
component.
The server-side AJAX request processing is provided by a RenderPhaseListener
instance. From the perspective of a server-side developer, the requests are a little different from other AJAX requests in that they usually do not go through all the phases of the JavaServer Faces technology life cycle. The following code shows how the RenderPhaseListener
instance handles an AJAX request:
private void handleAjaxRequest(PhaseEvent event) {
FacesContext context = event.getFacesContext();
HttpServletResponse response =
(HttpServletResponse)context.getExternalContext().getResponse();
..
if ("complete".equals(action)) {
String method = request.getParameter("method");
try {
//get completion items
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
//Code to write XML response goes here...
event.getFacesContext().responseComplete();
return;
} catch (EvaluationException ee) {
ee.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Code Example 3: PhaseListener code to handle AJAX requests
One thing the developer needs to remember is that the content type must be set to "text/xml"
and the Cache-Control header also needs to be set to "no-cache
" as shown in the following code snippet, which is taken from Code Example 3.
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
The call to event.getFacesContext().responseComplete()
following the creation of the XML response causes the rest of the JavaServer Faces life cycle to be skipped.
The response is returned to the XMLHttpRequest
callback method, which updates the HTML DOM with the list of potential city names.
The end user might then select a relevant city name from the list returned from the AutoCompleteTextField
component and click on the Update button, which causes the form to be submitted to the FacesServlet
instance, after which the SessionBean
object is updated.
This strategy makes it easy for page authors to add AJAX-enabled components to a JavaServer Faces page. As long as the component is added to the application, all they need to do is include the corresponding component tag in the page.
The component developer can make the component more customizable by allowing external JavaScript files to override the embedded file as well as allowing the use of external style sheets. One way to do this is to include JavaScript and CSS properties on the component that the user can override. For example, the AutocompletionTextField
component can have an ondisplay
property that lets users of the component specify JavaScript code that is executed when an item is selected from the popup. Similarly, a selectedItemStyle
property could let the user specify a CSS or a CSS style class to be applied to selected items in the popup.
This approach is used by the AJAX Progress Bar for JavaServer Faces Technology. As with Strategy 1, FacesServlet
is used to orchestrate all the processing of AJAX requests and all the serving of script files. The main difference between Strategy 1 and Strategy 1A is the use of POST to submit the actual view state back to the server. Doing this requires the AJAX-enabled components to abort normal page processing to prevent rendering of the entire JSP page for AJAX requests. This is done the same way as in Strategy 1, using the responseComplete
method on the FacesContext
instance. Another difference in the architecture of the progress bar example is that the PhaseListener
instance is responsible only for rendering the script, whereas ProgressBarRenderer
writes out the response XML that is handled by the AJAX JavaScript code.
Another complexity resulting from the polling nature of the progress bar is the need for a closure to the function passed to the setTimeout
JavaScript method. The progress bar uses the technique described by Richard Cornford at http://jibbering.com/faq/faq_notes/closures.html. This is a very handy technique when coding AJAX-enabled components.
Performance should be considered when determining how much view state is associated with each request relative to the volume of AJAX requests being processed when using this approach. While this approach is more tightly coupled with the JavaServer Faces life cycle and therefore provides ready access to Faces objects, this tight coupling could result in a performance degradation compared to the strategy of using a separate AJAX controller, which would not have a tight coupling to the view state.
In this strategy the JavaScript rendered by the JavaServer Faces component communicates with a separate servlet. All asynchronous requests will therefore be handled outside of the JavaServer Faces technology life cycle processing. However, the separate servlet can look up the FacesContext
instance and evaluate value binding and method binding expressions so that it can do such things as find managed beans and make calls to them.
Figure 2 illustrates this strategy. The numbered steps following the figure explain what is happening in the figure.
Figure 2: Architecture of a JavaServer Faces Component with separate AJAX Controller
The following list describes the interactions shown in Figure 2:
AutoCompleteTextRenderer
.
faces/autocomplete-script
, which is mapped to the FacesServlet
instance. This instance processes the RenderPhaseListener
instance, which recognizes the URL and returns the component.js
page containing the client-side JavaScript code necessary for the AJAX interactions. After the component.js
page is returned, the RenderPhaseListener
instance stops contributing to the JavaServer Faces technology life cycle processing for now.
onkeypress
event is generated. The JavaScript function mapped to this event creates an XMLHttpRequest
object and configures it with the URL to the AjaxControllerServlet
instance. This URL is autocomplete&id=San
. The id=San
part of the URL is a URL parameter in which San
is the user input that is matched against the list of cities.XMLHttpRequest
object makes the call to the AjaxControllerServlet
instance, and HTML page events continue to be processed by the page. The AjaxControllerServlet
instance looks up the AutoCompleteTextField
component and gets a list of potential completion items.
AjaxControllerServlet
instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest
object. The XMLHttpRequest
object then calls the XMLHttpRequest
callback function.
XMLHttpRequest
callback function updates the HTML DOM with the list of city names, which are contained in the returned XML document. At this point, users can select a city name, and when they do, the form element's value is set with the selected value.FacesServlet
instance, which updates the SessionBean
object with the address information entered by the user. This strategy provides a clean separation of the AJAX interaction logic and the traffic that is usually handled by the FacesServlet
instance. Each AJAX request is a new request that the JavaServer Faces technology life cycle does not see, meaning that AjaxControllerServlet
does not have access to the JavaServer Faces page view state. The AJAX servlet can communicate with the managed beans and evaluate expressions by looking up the FacesContext
instance and calling the corresponding code.
The strategy is similar to Strategy 1 in that all "plumbing" is generated by the JavaServer Faces component itself. In this strategy, the JavaServer Faces component is depending on a separate dedicated servlet to handle the JavaScript events it is adding to the rendered HTML. Updates to the JavaServer Faces model data occur outside the scope of the JavaServer Faces technology life cycle request/response processing, and so care should be taken that model data is consistent.
In this strategy, no custom JavaServer Faces components are used. As in Strategy 2, a separate dedicated AJAX servlet is used, and custom JavaScript code is added to the JSP page for the purpose of communicating with the servlet. Developers are responsible for all the "plumbing". This means that they must provide the code that handles JavaScript events associated with the JavaServer Faces components, makes asynchronous calls, and updates the HTML document when responses arrive.
Figure 3 illustrates this strategy. The numbered steps following the figure explain what is happening in the figure.
Figure 3: Retrofitting an Existing JavaServer Faces Application
The following steps explain the architecture of the JavaServer Faces application shown in Figure 3:
onkeypress
event is generated. The JavaScript function mapped to this event creates an XMLHttpRequest
object and configures it as a URL to the AjaxControllerServlet
instance. This URL is autocomplete&id=San
. The id=San
part of the URL is a URL parameter in which San
is the user input that is matched against the list of cities. After the XMLHttpRequest
object makes the call to AjaxControllerServlet
, the page continues to process HTML page events.
AjaxControllerServlet
looks up the CitiesBean
managed bean and gets a list of potential completion items.
AjaxControllerServlet
instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest
object. The XMLHttpRequest
object calls the XMLHttpRequest
callback function.
XMLHttpRequest
callback function updates the HTML DOM based on the contents of the XML document that was returned.
FacesServlet
instance, which updates the SessionBean
object with the respective address information. Figure 3 is very similar to Figure 2, which illustrates Strategy 2, with one exception: In Strategy 2, the servlet is provided with the JavaServer Faces component and handles "generic" traffic. In Strategy 3, the JavaScript and the servlet are provided by the application developer. Notice that the servlet talks about the application-specific list of cities, whereas in Strategy 2 the servlet handles generic items. This is because strategy 2 is designed to be reusable in that the solution can be utilitzed to handle various auto completion use cases, such as completing city names, state names, or people names, whereas Strategy 3 is bound to the specific use case of completing city names.
This strategy has the advantage of not relying on any special AJAX-enabled JavaServer Faces components, and therefore, any existing JavaServer Faces application can be retrofitted with custom JavaScript and a special servlet to handle AJAX behavior. However, like Strategy 2, the use of a separate servlet makes calling JavaServer Faces methods more difficult. Furthermore, it is impossible to get access to the view state of the JavaServer Faces page.
This strategy allows for more customization of the JavaScript code that provides the AJAX interaction, but it is also more difficult to maintain and requires that the page author be familiar with AJAX interactions and JavaScript. The page author is responsible for matching the events generated by the HTML elements to the JavaScript functions. Doing this might be difficult for the average page author because the HTML elements are generated by a JavaServer Faces component renderer, and therefore the page author must be somewhat familiar with the server-side renderer code. In addition, the page author is also responsible for making sure that the AJAX updates to the HTML DOM match the HTML elements rendered by a JavaServer Faces component.
The application developer is responsible for the server-side processing of AJAX using a web component, such as a servlet. Updates to the JavaServer Faces model data occur outside the scope of the JavaServer Faces technology life cycle request/response processing, so care should be taken to ensure that model data updated by a server-side component is consistent with that managed by the JavaServer Faces technology runtime.
Below is a table that contains a list of trade-offs for each strategy.
Strategy 1: Custom JavaServer Faces Component that Renders Client-side AJAX Script and Processes AJAX Requests | Strategy 2: Custom JavaServer Faces Component for separate AJAX Controller | Strategy 3: No Custom JavaServer Faces Component: Developer-Managed JavaScript and AJAX Servlet | |
Where do I put the control logic? | In a PhaseListener instance. |
In a servlet or servlet filter. | In a servlet or servlet filter. |
Does the page developer need to provide client-side AJAX JavaScript and map to form elements? | No. The JavaServer Faces component renderer will render the necessary JavaScript. | Yes. Additionally, care must be taken to ensure the form elements mapped to by the script match those rendered by the target JavaServer Faces component. | |
Is the solution re-usable? | Yes. However, the JavaScript included with the JavaServer Faces component must be written such that it can process requests from multiple components. There is a limited number of XMLHttpRequest objects that can be used in a page, so care should be taken with the JavaScript in this case also. |
No. Each JavaServer Faces page would require its own JavaScript. The JavaScript can be shared in a separate file, but portions such as the XMLHttpRequest callback function are generally bound to a specific use case. | |
Is the solution customizable? | Yes, provided the component provides customization hooks | Yes, but that is the problem. Each JavaServer Faces page contains a customized solution. | |
Does the component have access to the view state of a JavaServer Faces page? | Yes, but there are subtle differences in how the view state can be made accessible depending on the version of the JavaServer Faces technology runtime you are using. | No | No |
Can the AJAX control logic for a JavaServer Faces component be overridden? | Yes. The control logic is in a PhaseListener instance, which would need to be replaced. This would require repacking the component. |
Yes. The controller is a servlet that can be modified or replaced. | |
Can the AJAX control logic access managed beans and evaluate expressions? | Yes. Each request is a JavaServer Faces request that has ready access to managed beans and expressions. | Yes. The AJAX control logic code can access managed beans and evaluate expressions by looking up the FacesContext instance. This requires a few lines of code. | |
Can I leverage existing JavaScript libraries? | Yes. Either the JavaScript must be embedded with the JavaServer Faces component or the component must be capable of using external JavaScript. | Yes. | |
Can I leverage the AJAX control logic outside a JavaServer Faces technology runtime? | No. | No. | Yes. Servlets and servlet filters do not require a JavaServer Faces technology runtime; although, if portions of the AJAX control logic are bound to managed beans or are evaluating expressions in the JavaServer Faces technology runtime, this would not be possible. |
The JavaScript should be placed in a separate file as mentioned above, to ensure that the JavaScript contents can be cached by the browser.
While the JavaServer Faces component can be used multiple times in a page, the JavaScript inclusion reference should be emitted exactly once -- before the first usage. A good way to do this is to modify the renderer that emits the script
tag so that it stores a flag for itself in the request map. This flag records whether the renderer has written the script out for the current page yet. The following code snippet demonstrates how to add code to the renderer that will store the flag and check its value to determine if the script
tag has been rendered:
...
private void renderScriptOnce(ResponseWriter writer, UIComponent component, FacesContext context)
throws IOException {
// Store attribute in request map when we've rendered the script such
// that we only do this once per page
Map requestMap = context.getExternalContext().getRequestMap();
Boolean scriptRendered = (Boolean)requestMap.get(RENDERED_SCRIPT_KEY);
if (scriptRendered == Boolean.TRUE) {
return;
}
// Render script and CSS here
...
}
private static final String RENDERED_SCRIPT_KEY = "bpcatalog-ajax-script-rendered";
One issue that developers must deal with is how to organize their workspace. What should the project conventions be? Generally, this is covered by existing project conventions for JavaServer Faces technology modules and applications. For example, the faces-config.xml
file and the TLD files for JavaServer Faces technology are usually kept in the src/conf
directory.
But where should you keep the JavaScript files? Should they be kept with the source code files? In most cases, we recommend that the JavaScript files be kept in the src/resources
directory. In some cases, you might be using a pre-existing library of JavaScript files, which might be reusable even without the JavaServer Faces components, such as in the case of making a JavaServer Faces component that uses some of those existing JavaScript files. In this instance, the JavaScript library should be viewed as a separate project and should be used only by the JavaServer Faces application.
Another issue to consider is how to package these JavaServer Faces components and JavaScript artifacts into a WAR file or EAR file. The packaging is mostly the same as for other JavaServer Faces modules and applications, but you need to consider where to put the AJAX artifacts, which include the javascript files. Generally, it is recommended that you place the AJAX artifacts with the compiled classes so that the JavaServer Faces components can load them by calling getClass().getResourceAsStream()
.
In order to use the components within an IDE, there are additional packaging considerations, such as how to make properties visible in IDE property sheets. These considerations differ from IDE to IDE (until this hopefully gets standardized with JSR 273). For a description of how to package your JavaServer Faces component for Sun Java Studio Creator, read http://developers.sun.com/prodtech/javatools/jscreator/reference/techart/jsfcomplibrary.html .
FacesServlet
and PhaseListener
style to keep things self contained. For more details on this solution see the JavaServer Faces Technology Auto-Completion Component with AJAX: Design Details and AJAX Progress Bar for JavaServer Faces Technology: Design Details.
May 25, 2005
The JavaServer Faces (JSF) specification provides a set of components with which application developers can build Java server applications. However, it is extremely likely that developers will want to extend JSF by creating custom components. Here we'll explore the process of creating new JSF components, highlighting common "gotchas" and providing guidelines for defining good components. We'll also look at the need for providing design-time metadata, so that component developers can ensure an excellent design-time experience for their custom components.
Before we start looking at components, it is worthwhile to elaborate on "why another framework?" When it comes to the architecture of JSF, it is not a new thing. For example, Apache's Tapestry and Oracle's ADF UIX framework have been around for quite some time and have proven to be very successful. JSF brings what other similar view technologies do not have: backing of a standard specification (JSR 127).
If we look at where we are today, most Web applications are stuck in the 1990s, when much too much effort was put into basic plumbing and not enough into high-level components. Basically, there is limited abstraction or no abstraction over the markup making development of Web applications cumbersome and the applications themselves hard to maintain. A lot of work is forced into the application to make it rich and interactive, with various technologies from applets, plug-ins (Flex), DHTML, and JavaScript. Together, these technologies can make up a very interactive and powerful Web application, but how do you maintain such an application? How do you reuse what you have built? Do you need multiple tools to achieve your goal of an interactive Web application?
These questions lead back to JSF and what it brings to the table—a best-of-breed J2EE framework—and the fact that every major J2EE tool vendor such as Oracle, IBM, Sun, and Borland is behind JSF.
Potential Beneficiaries
So, who in the IT community would gain from using, developing, and deploying JSF applications instead of using proven but nonstandard technologies such as Tapestry, Cocoon, and Struts? Let's have a look.
End users. End users will probably not notice much of what is happening behind the scene, but they will notice that Web applications are richer in functionality as we move ahead with JSF.
Business developers. JSF gives business developers the means to build powerful enterprise Web applications by using an open standard (Java) with the ease and productivity that Visual Basic has been providing to its developers. The fact that more components will be available on the market, free or for a fee, means that business developers will have access to richer components—including dynamic menus, scroll bars, tables, and so on—that have so far been available only to those with expertise in DHTML, JavaScript, and the like. These components will not only provide business developers with richer functionality but will also improve their productivity in building Web applications.
Component developers. Components, components, components: these are the golden nuggets that will make or break the success of JSF. Some vendors are already producing components for external use and for open source communities such as MyFaces (see Resources). But, for JSF adoption to increase we need more components—lots of them—and with richer functionality.
The dearth of existing components and the need for richer component sets mean that getting into the business of providing JSF components is a good opportunity. JSF also allows component authors to sell components based on a recognized J2EE-standard framework (instead of trying to convince customers to use a homegrown framework). Besides the business aspect of creating JSF components, there are also benefits to creating components by using JSF. JSF provides built-in support for aspects that are hard and cumbersome to implement, such as event handling, state saving, expression language, and a standard way to make custom components extensible.
Tool developers. Tool developers need a formal standard that not only makes life easier for end users but also allows the developers to design and develop tools that will ease development of Web applications, or as they are called in this early part of the twenty-first century, J2EE applications.
As you are probably aware, JSF uses different classes for providing component functionality and for rendering the component. Before getting your hands dirty building your first set of components, search the Web for existing components.
There should be a fair amount of components already out there, and in most cases, you can probably get away with writing a new renderer for an existing component. If the component you are looking for cannot be found, it is time to build your own. To build a new component, you should make sure it has distinct server-side behavior and introduces a new behavior, functionality, or definition. If the component exists and you need a new appearance, you just need to create a new renderer (see Figure 1).
Good and Bad of It
An example of good separation between behavior and appearance is the UISelectOne component provided by the JSF Reference Implementation. This component has a distinct behavior and three different renderer types: ListBox, Radio, and Menu. Potentially, you can add other types of renderers that have different appearances but the same behavior.
What distinguishes a good component from a bad component? A good component should have generic properties, event listeners, and specific behavior. The component should not contain markup-specific attributes, such as the "href" attribute; should not write markup or get request parameters; and should work with server events, not client events. It is important to isolate the presentation information from the underlying server-side component (see Table 1).
In addition to the Table 1 information, using JSF facets enhances business developers' user experience developing applications with JSF components. A facet is a kind of child component that has a specific purpose defined by its name. A facet can be viewed as an empty placeholder that can take one child component. A parent component can have multiple facets; an example is a component called panelPage provided by Oracle's ADF Faces component set. This component uses facets to render its children in predefined locations such as the corporate logo (CORPORATE_BRANDING facet) or the copyright statement (COPYRIGHT facet).
Renderers have their own classes and are therefore also subject to good and bad implementations. Writing a good renderer means basically reversing the list of dos for writing a good component. What is really important is that you do not give model access to the renderer. You should also try to use the ResponseWriter as often as possible, which will give tools a better chance of visualizing your component in a WYSIWYG editor. Here is an example of using the ResponseWriter:
public void encodeBegin(
FacesContext context,
UIComponent component) throws
IOException
{
ResponseWriter out =
context.getResponseWriter();
//Pass the component so that
//the ResponseWriter can use
//it.
out.startElement(
"span", component);
//Pass the "styleClass"
//attribute so that it can be
//matched.
Object style =
component.getAttributes().
get("styleClass");
out.writeAttribute("class",
style, "styleClass");
}
You also need to make sure you register your renderer with a render kit. The most widely used rendering language is HTML, but this characteristic doesn't mean that you must use the HTML render kit; you can use any rendering language of your choice, such as SVG or XML.
There are some areas that need improvement and a few areas or gotchas that are not yet covered or will not be covered by the JSF specification. One example is the lack of a strategy for a consistent look and feel. There is currently nothing in the specification that outlines or defines a standard for implementing "themes" or "skinning." An area of improvement would be to make it easier to write a renderer. Today you need to write Java code to create a renderer, but it should be possible to come up with a more declarative solution for writing renderers similar to what is available in ADF UIX.
There is also no support for registering a renderer with multiple render kits and no tool support for testing components across multiple implementations such as MyFaces or Sun's JSF Reference Implementation.
The Good, the Bad, and the Ugly
As a component developer or vendor, you are not locked into any IDE; in fact, if you prefer to use a text editor such as Emacs or VI, you can do so. JSF simplifies J2EE development because it will be easier for tool vendors to create a design-time experience for business developers similar to Visual Basic, using custom-designed and reusable components. Component developers may have the following questions: How do I get tools to support my custom components? Do I have to add anything beyond the required renderer so that my components can utilize all design-time features or display nicely in the tool's visual editor?
The good. As you probably already know, there are plenty of tools available today with various levels of JSF support, and all major tool players—Oracle, IBM, Sun Microsystems, and Exadel—are working feverishly to improve the support for JSF. The JSF expert group clearly thought about tool support when they designed the faces-config.xml file. This XML file is an invaluable source of information that contains, for example, available components and their properties and facets. Not only is the XML file format easily digestible by tools, but also tools can automatically detect the location of a faces-config.xml for a set of components. Tools can, based on information provided in the faces-config.xml file, expose the set of available components at design time.
The bad. The bad thing is that faces-config.xml and TLDs are not very expressive and cannot convey interesting constraints such as valid values, or component enumerations. An example is the Oracle ADF Faces component
The ugly. What could turn ugly is if tool vendors define their own proprietary ways of providing design-time metadata. Tool vendors are already asking for additional metadata from component authors so that components can fully utilize the design time, which will lead to component authors providing proprietary solutions for each tool vendor.
The hope. The hope is that the JSF community can agree on standard design-time metadata and that component authors will invest in providing metadata for their components. The most likely way of defining such a standard is through the Java Community Process (JCP). Oracle is working with other JCP members with the goal of proposing a new JSR that would define a standard set of design-time metadata items for JSF components and a mechanism for providing such metadata.
Although the faces-config.xml language allows a component author to express a great deal of information about their components and renderers, it only has standard syntax for expressing a very minimal set of design-time metadata (mainly display-name, description, and icon). Fortunately, the faces-config.xml language is designed with convenient extension points that can be used to hold additional information.
Strong Community
As previously discussed, there is not yet a standard mechanism for supplying additional design-time metadata. However, one proposed solution would utilize the extension points in faces-config.xml (see Listing 1). If you are interested in this issue, there is a thread on the Sun's JavaServer Faces Technology forum devoted to discussing design-time metadata for JSF components.
A crucial ingredient for the continuation of JSF's success and progress is a strong community. If you look at what ASP.Net has done and evaluate what makes this proprietary framework so successful, there is one thing that always pops up: the ASP community. Its community sites have forums with hundreds of daily posts as well as tutorials and content that are updated daily. There are component galleries with free hosting of custom components, which are integrated with the development tools. There are also free versions of basic tools supporting ASP development.
Some of this type of groundwork has already started with projects such as MyFaces and information sites such as JSFCentral, but this is not enough. We cannot hope that a small group or a single person can provide us with a complete set of community sites including daily posts, tutorials, and contents. Now is the time to get involved in Faces!
The JSF community needs one or more neutral hosting sites that provide content such as tutorials, forums, and announcements—and, of course, component galleries. Another area in which the community can provide help in is testing tools. In the spirit of Cactus and JUnit, the community could provide testing tools with a different approach, such as test components and not markup, and send simulated events. For anyone eager to get involved, this possibility would make a good open source project.
Skinning and themes are areas that noncoders could ideally participate in. Noncoders could provide style abstractions to mirror component abstractions using styles, icons, text/translations, and templating.
There is a future beyond HTML; eventually, component authors will provide higher-level components with better customization. These components will be based on other client-side technologies, such as DHTML, SVG, Flex, XUL, and Web Forms (see Figure 2).
The message here is, think components, not markup! JSF is not exclusively for HTML, it is not exclusive to hard-core developers, and it is not exclusive to business developers. There are plenty of opportunities for all types of developers. Finally, don't forget the metadata, and make sure you get involved early in the JSF community.
About the Author
Jonas Jacobi is a senior product manager for JDeveloper at Oracle.