Building Custom JSF UI Components
One of the key strengths of JavaServer Faces (JSF) is that not only does it provide substantial technology for easy, out of the box component based J2EE Web applications assembly, but it also is a very flexible API which allows for a wide breadth of customizations in numerous and innovative ways. This article introduces and explores the component developer's experience of building custom JSF user interface (UI) components.
Overview
One of the key strengths of JavaServer Faces (JSF) is that not only does it provide substantial technology for easy, out of the box component based J2EE Web applications assembly, but it also is a very flexible API which allows for a wide breadth of customizations in numerous and innovative ways. This article introduces and explores the component developer's experience of building custom JSF user interface (UI) components.
Intended Audience
First off, this article is intended for component developers who already have an understanding of the overall JSF application development process but want to learn how to start developing their own custom JSF UI components. For those who are still new to JSF development, readers are highly recommended to first consult JSF application tutorials such as Rick Hightower's article on basic JSF development:
http://www-106.ibm.com/developerworks/library/j-jsf1/
Or my earlier JavaPro article, which also introduces JSF:
http://www.fawcette.com/javapro/2004_01/magazine/features/cschalk/
In contrast to those introductory JSF articles, this article introduces the more advanced task of building custom JSF UI components by first explaining all of the "moving parts" associated with custom UI Components starting with a simple custom "HelloWorld" UI Component as an example.
The article then shows how to extend the simple "HelloWorld" component into more useful examples that show live stock quote information using a Web service. At the end of the article readers will have an appreciation of what exactly are the key tasks associated with building custom UI Components along with a full understanding of all the various sub-components that make up a typical "JSF UI Component".
When to Customize JSF
Before jumping into custom JSF component development just for the sake of it, some research should be done on whether custom development is necessary at all and at what level it is needed. As mentioned before, JavaServer Faces' base UI component library actually provides a modest amount of practical UI and non-UI components in its specification. In addition to visible components such as input/output fields, menu components and form buttons the JSF specification also has non-UI components which handle things like basic validation and/or data conversion. In many cases a developer may find that he/she may only need to customize a specific sub-component such as a new validation method as opposed to building an entirely new UI Component.
An easy google search or browsing through sites such as jsfcentral.com many usable and often free component libraries (and implementations) that can be experimented with. Some of the more popular and powerful ones include:
- MyFaces - A general purpose and open source component library and implementation. - http://myfaces.apache.org
- Oracle's ADF Faces - A rich library of useful JSF UI Components - http://www.oracle.com/technology/products/jdev/htdocs/partners/addins/exchange/jsf/index.html
- JScape's WebGalileo Faces - http://www.jscape.com/webgalileofaces/
Once the decision has been made to build some custom components as opposed to just using some existing ones, here's how to get started.
The "Moving Parts" of a JSF UI Component
The term "JSF UI Component" is generally used to describe a set of sub-components which each which perform their own specific task such as rendering the component, validating its input, and/or performing any data conversions (such as a String to a Date conversion). This may often give the impression to a novice JSF developer that JSF component development is fairly complicated and possibly tedious, but this is also JSF's strength in that each UI Components' sub-components can be individually customized and re-configured into new and varied combinations. In fact it is JSF's flexible API and it's "pluggable renderering" technology which allows UI Components to written for multiple Web clients thus drastically decreasing the complexity for the Web developer of assembling multi-client Web applications. Let's review the various "moving parts" of what constitutes a JSF UI Component.
The general term "JSF UI Component" refers to a collection of the following:
- UIComponent Class - A Java class derived from either the UIComponentBase or extended from an existing JSF UIComponent such as outputText. This is the actual Java class representing the core logic of the component. It can optionally contain the logic to "render" itself to a client, or rendering logic can be separated into a separate "renderer" class.
- Renderer Class - This is a class that only contains code to render a UIComponent. Rendering classes can provide different renderings for a UI Component. For such as either a button or hyperlink for the UICommand component. Renderers can also provide different rendering options for the same UI Component on different client types such as browsers, PDAs etc. This allows JSF the ability to run on any client device providing a corresponding set of renderer classes are built for the specific client type.
- UI Component Tag Class - This is a JSP tag handler class that allows the UI Component to be used in a JSP. It can also associate a separate renderer class with a UIComponent class. (Note: The UI Component Tag handler class is only required for usage in a JSP deployment environment. Keep in mind that UI Components can also be used in non-JSP environments.)
- Tag Library Descriptor File - This is a standard J2EE JSP tag library descriptor (tld) file which associates the tag handler class with a usable tag in a JSP page. (Required only For JSP usage only.)
- Associated helper classes - These include a collection of standard (included in JSF RI) or custom helper classes such as Converters, Validators, ActionListeners etc., that can be programmatically bound to UI Components. For example a JSF UI Input Field component can be associated with a built-in number range Validator which ensures that an entered number is within a certain range. These helper classes can also be customized to perform any type of validation not provided out of the box with the JSF RI. For example a custom credit card Validator could be used with a JSF Input field to validate credit card numbers or a custom Converter could be used to convert currencies.
A Simple "HelloWorld" Custom UI Component Example
Before showing how to build a "Hello World" custom component, let's review quickly how to use it. To invoke the component we have a JSF enabled JSP page with an extra taglib directive specifying a custom tag library (tld) which contains the custom tag, "jsfhello", which is associated with our custom UI Component. To use the jsfhello tag, we simply place it into the body of the JSP and set an optional "hellomsg" attribute.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
When the page is run, the JSF UI Component displays the "Hello World" message provided in the "hellomsg" attribute along with the current date and time. We'll review the details of the tag library and how the tag handler class invokes the UI Component shortly, but first let's review how to build the core UI Component class.
Building the"HelloWorld" Custom UI Component Step 1: Creating a UIComponent Class
For our HelloWorld example, our UIComponent will extend the UIComponentBase abstract class, which is provided in the JSF specification, and render a formatted HelloWorld message. Note: We could have also based our new UI Component class on the concrete UIOutput base class which is basically the same as the UIComponentBase with the exception that it has a "value" attribute. Since we aren't using one, we'll simply base it on the UIComponentBase abstract class.
package tss.hello; import java.util.Date; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import java.io.IOException; import javax.faces.context.ResponseWriter; public class HelloUIComp extends UIComponentBase { public void encodeBegin(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); String hellomsg = (String)getAttributes().get("hellomsg"); writer.startElement("h3", this); if(hellomsg != null) writer.writeText(hellomsg, "hellomsg"); else writer.writeText("Hello from a custom JSF UI Component!", null); writer.endElement("h3"); writer.startElement("p", this); writer.writeText(" Today is: " + new Date(), null); writer.endElement("p"); } public String getFamily() |
As you can see, this custom UI Component renders a formatted Helloworld message using and encodeBegin() method. Also notice that we have defined a method getFamily(). This is actually required for UI Components that extend UIComponentBase since it could come from any component family. Since in our example we won't be creating a new family of components, we can just return any String value such as "HelloFamily". Before examing the code further, a quick note on encoding and decoding.
A Quick Note on Encoding Methods
UI Components and/or associated renderer classes use encode*() methods display themselves to a client. The above HelloWorld example simply renders an HTML header (h3) with the custom hello message (if supplied) along with a paragraph containing the current date and time. For this component a single encodeBegin() (or encodeEnd() ) method is all that is needed to render the complete tag since it does not contain any children. The encodeBegin actually renders the beginning or the complete (bodiless) tag. UI Components with children tags/components will override encodeChildren() along with encodeEnd(). The encodeChildren() method allows children components to be rendered and encodeEnd() renders the closing parent tag.
A Closer Look at the encodeBegin() Method
Upon closer examination of the encodeBegin() method, we see that it receives the FacesContext as an argument. An extension to the servlet and JSP context, the FacesContext provides access to the many useful objects for JSF/JSP/Servlet development. In this example, we simply extract a "writer" object in order to "write" our rendered response back to the client.
public void encodeBegin(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); |
Next we get the value of the attribute "hellomsg" which is passed from the tag from our JSP page using getAttributes(). The getAttributes method comes from the UIComponentBase class which is the base class for all UI Components.
String hellomsg = (String)getAttributes().get("hellomsg"); |
Rendering the HTML message is done using the writer methods in the code:
writer.startElement("h3", this); if(hellomsg != null) writer.writeText(hellomsg, "hellomsg"); else writer.writeText("Hello from a custom JSF UI Component!", null); writer.endElement("h3"); writer.startElement("p", this); writer.writeText(" Today is: " + new Date(), null); writer.endElement("p"); |
which when executed displays a formatted HTML "HelloWorld" message to the client.
Note: You'll notice that when the attribute (hellomsg) or "property" is written to the client, an additional String value representing the name of the property "hellomsg" is also included as an argument to the writeText() method. The idea behind this is to provide development tools environments the ability to display the name of the property in a visual editor. Leaving the second argument null is also acceptable and will not harm the execution of the component.
Step 2. Registering the custom UI Component in Faces-config.xml
Before moving on to building a JSP tag handler and a TLD file, we'll add a required entry for our custom component in the faces-config.xml file. The syntax for adding our custom UI component is:
... <faces-config xmlns="http://java.sun.com/JSF/Configuration"> |
The component-type is the "JSF recognized" name of our custom component: "tss.hello.JsfHello". (We'll refer to this later in our tag handler.) The component-class is the actual class path address of our UI Component.
Step 3. Building a Custom JSP Tag Library
In order to be able to use our custom component in a JSP, we need a custom tag library comprised of a tag library descriptor file (TLD) along with references to taghandlers classes. (This example uses just a single tag handler.)
Building the Tag Handler
For JSF component development, the JSP taghandler class is derived from javax.faces.webapp.UIComponentTag. It's main purpose is to:
- Associate a JSP callable tag (handler class) with the UI Component.
- Associate a separate renderer class (if needed) to the UI Component.
- Set the properties from the submitted tag attribute values to the UI Component.
Here is the source code for our tag handler class:
package tss.hello; import javax.faces.application.Application; import javax.faces.webapp.UIComponentTag; import javax.faces.component.UIComponent; import javax.faces.el.ValueBinding; import javax.faces.context.FacesContext; public class FacesHelloTag extends UIComponentTag { // Declare a bean property for the hellomsg attribute. public String hellomsg = null; // Associate the renderer and component type. public String getComponentType() { return "tss.hello.JsfHello"; } public String getRendererType() { return null; } protected void setProperties(UIComponent component) { super.setProperties(component); // set hellomsg if (hellomsg != null) { if (isValueReference(hellomsg)) { FacesContext context = FacesContext.getCurrentInstance(); Application app = context.getApplication(); ValueBinding vb = app.createValueBinding(hellomsg); component.setValueBinding("hellomsg", vb); } else component.getAttributes().put("hellomsg", hellomsg); } } public void release() { super.release(); hellomsg = null; } public void setHellomsg(String hellomsg) { this.hellomsg = hellomsg; } } |
The first thing to note is the "hellomsg" bean property of type String along with its associated getter and setter methods at the bottom of the class. The hellomsg bean property, "hellomsg", is also an attribute in the JSP tag. (<sf:jsfhello hellomsg="Hello world!"../>)
Next we see two methods which associate this tag handler with our registered UI Component: "tss.hello.JsfHello" as well as associate the renderer. Since we don't have a separate renderer class, this statement returns a null value.
// Associate the renderer and component type. public String getComponentType() { return "tss.hello.JsfHello"; } public String getRendererType() { return null; } |
The next method, setProperties(), sets the incoming values from the JSP tag by first calling the parent class' setProperties method along with custom code to set the value from the hellomsg tag attribute. Notice that a check is first done to see if the incoming attribute is a "ValueReference", which takes the form of a JSF EL expression: #{Bean.Property}. If a "ValueReference" is detected, it uses slightly different logic to set the application's value binding.
protected void setProperties(UIComponent component) { super.setProperties(component); // set hellomsg if (hellomsg != null) { if (isValueReference(hellomsg)) { FacesContext context = FacesContext.getCurrentInstance(); Application app = context.getApplication(); ValueBinding vb = app.createValueBinding(hellomsg); component.setValueBinding("hellomsg", vb); } else component.getAttributes().put("hellomsg", hellomsg); } } |
Asides from the hellomsg getter and setter methods the only method required in the taghandler class is the release() method which resets the bean properties back to an unused state.
public void release() { super.release(); hellomsg = null; } |
and that's it for our tag handler class. Next we'll quickly review the JSP Tag Library Descriptor (TLD) required for this tag.
Step 4. Building the Tag Library Descriptor File
In order for us to use our custom JSP tag handler class, we need to create an associated TLD file which contains the tag entry associated with the tag handler class. Here is an example of the TLD file needed for this tag. The TLD associates the tag name, "jsfhello" with the tag class "tss.hello.FacesHelloTag" along with it's associated attributes. For our tag, we include our custom attribute, "hellomsg", along with the superclass' core attributes: "id", "binding" and "rendered".
<?xml version="1.0" encoding="ISO-8859-1" ?> |
Following standard J2EE architecture, the custom TLD file is placed in the /WEB-INF/. sub-directory of the J2EE Web Module containing the application.
Running the simple HelloWorld Example
Just to review, our custom JSF component java classes must be compiled and available on the classpath of a J2EE Web Module. The Web Module must also have in it's class path the required JSF runtime jar files which include: jsf-api.jar, jsf-impl.jar along with the commons jar files (commons-beanutils.jar, commons-collections.jar, commons-digester.jar, commons-logging.jar). The other two required jar files come from JSTL (jstl.jar and standard.jar).
For more info on the Commons and JSTL libraries refer to the Jakarta Apache Project website: http://jakarta.apache.org/ .) For a runtime deployment, all the required jar files must be placed in the /WEB-INF/lib subdirectory of your J2EE Web Module.
Assuming your runtime environment is properly configured, you can test the simple JSF Hello World custom component by first creating a JSF enabled JSP page and then adding the taglib directive and dropping the tag into your page. (As explained before..)
When the JSF page is run, you will see the execution of your HelloWorld JSF Custom Component!
Extending the HelloWorld Custom Component
Now that we've created a simple HelloWorld component, we can now expand upon this very easily. One straightforward extension would be to make the HelloWorld component call a Web service for a specific stock symbol and print out it's value. So instead of providing a hellomsg, our tag/component attribute will contain a stock symbol. The renderer code will then query a stock quote Web service (made available to us via another class) with the symbol provided by the tag and print the current price in the response.
Here's how to modify the HelloWorld Custom Component to display a live stock quote using a Web service.
Create a Web Service Proxy Client
In order to call a Web service from java, you'll need to create a proxy java client which enables any other Java class the ability to call a stock Web service. Many free stock quote Web services are available on the Internet. This example uses the "Delayed Stock Quote Service" available from Xmethods.com. The actual Web Proxy creation step is left to the reader to implement since it is out of the bounds of JSF development however several Java Integrated Development Environments such as Oracle JDeveloper, can automatically generate the necessary client code which enables communication to a Web service.
Oracle JDeveloper 10.1.3 Preview's Web Service Proxy Wizard
Once a Web service Proxy class is created, we'll modify our hellomsg attribute to become a stock symbol attribute and call the Web service with this value and then print it out. This involves the following changes:
Create or Modify the HelloWorld UIComponent Class to Work with "Symbol" Attribute
Change or create a new UIComponent class which refers to a stock symbol attribute, "symbol", instead of the "hellomsg".
public class StockUIComp extends UIComponentBase { public void encodeEnd(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); String symbol = (String)getAttributes().get("symbol"); ... |
Note: If you create a new UI Component such as StockUIComp, don't forget to register it in your faces-config file ( You could use the name: "tss.hello.JsfStock" mapped to the classpath address "tss.hello.StockUIComp").
Once you've retrieved the attribute value for the stock symbol, you can call your Web service proxy class to get the current price:
//get stock price using generated Web service Proxy String stockprice = NetXmethodsServicesStockquoteStockQuotePortClient.getStockPrice(symbol); |
Rendering the the stock price is easily done with the following code:
writer.startElement("p", this); if(symbol != null) writer.writeText("The stock price for " + symbol + " is: " + stockprice + ".", null); else writer.writeText("You must provide a Stock Symbol by setting the symbol attribute.", null); writer.endElement("p"); |
The changes to the tag handler and TLD is a trivial search and replace of "hellomsg"" with "symbol". Your tag handler class will also have to refer to the different Stock UI Component: "tss.helloJsfStock", but the remaining code in the taghandler is basically the same as before with the "symbol" attribute replacing the "hellomsg" attribute:
public class StockTag extends UIComponentTag |
Once you've updated your TLD with a new tag "simplestock" mapped to the taghandler "StockTag", you can then add the new tag to your JSP:
<p>The SimpleStock UI Component:</p> |
Here's the output for the HelloWorld Component along with the new Stock UI Component:
Extending the Stock UI Component Further
Now that we've extended the HelloWorld UI Component into something useful, let's further extend the stock component to make it more user friendly. Our modified stock component will now render an input field along with a submit button. This will allow users to enter a new stock symbol at runtime as opposed to hardcoding the attribute value in the JSP source.
This modification will introduce how to build a decode() method which is used to "decode" the incoming form field values as opposed to simply retrieving tag attribute values. We will also extend our simple encode() method to now render in input field along with a submit button.
Back to the UI Component
This time we'll name the UI Component "HtmlInputStock" following the JSF specification's naming convention. This time we'll extend extend the class from the UIInput component since it will be accepting input.
public class HtmlInputStock extends UIInput { ... |
This component will have a more detailed encode() method. This time our encodeBegin() method will call separate encode methods to render the differerent elements: an input field, a submit button, and an output field which displays the returned price of the stock. Notice that I've add the identifiers ".inputbutton" and ".submitbutton" to the clientIds respectively when calling the encode methods. This will ensure that the names of the rendered HTML: fields will be unique on the client. In general this is good practice should you need to refer to the rendered name of the HTML field.
The encodeBegin() method is as follows:
public void encodeBegin(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); String clientId = getClientId(context); encodeInputField(writer, clientId+".inputbutton"); encodeSubmit(writer, clientId+".submitbutton"); encodeOutputField(context); } |
The encodeInputField() method is as follows:
private void encodeInputField(ResponseWriter writer, String clientId) throws IOException { // render a standard HTML input field writer.startElement("input", this); writer.writeAttribute("type", "text", null); writer.writeAttribute("name", clientId, "clientId"); Object v = getValue(); if (v != null) writer.writeAttribute("value", v.toString(), "value"); writer.writeAttribute("size", "6", null); writer.endElement("input"); } |
Notice the name of the input field is assigned the clientId. We'll use this later to identify the field when we decode the input.
The encodeSubmit() is rendered with:
private void encodeSubmit(ResponseWriter writer, String clientId) throws IOException { // render a submit button writer.startElement("input", this); writer.writeAttribute("type", "Submit", null); writer.writeAttribute("name", clientId, "clientId"); writer.writeAttribute("value", "Enter Stock Symbol", null); writer.endElement("input"); } |
And finally the encodeOutputField() which is similar to before:
public void encodeOutputField(FacesContext context) throws IOException { ResponseWriter writer = context.getResponseWriter(); String symbol = (String)getAttributes().get("value"); // The "value" attribute is used to pass the stock symbol. //get stock price String stockprice = NetXmethodsServicesStockquoteStockQuotePortClient.getStockPrice(symbol); writer.startElement("p", this); if(symbol != null) writer.writeText("The current price for " + symbol + " is: " + stockprice + ".", null); else writer.writeText("", null); writer.endElement("p"); } |
Now let's move on to the decode() method who's job is to parse the incoming field values and act upon it.
public void decode(FacesContext context) { Map requestMap = context.getExternalContext().getRequestParameterMap(); String clientId = getClientId(context); try { String string_submit_val = ((String) requestMap.get(clientId+".inputfield")); setSubmittedValue(string_submit_val); setValid(true); } catch(NumberFormatException ex) { // let the converter take care of bad input, but we still have // to set the submitted value, or the converter won't have // any input to deal with setSubmittedValue((String) requestMap.get(clientId)); } } |
Let's review some of the key statements in our decode() method. First our decode() method obtains a requestMap from the JSF Context.
Map requestMap = context.getExternalContext().getRequestParameterMap();
The requestMap is a container which allows us to access the values submitted in the http request.
The next statement extracts the clientId which is the unique identifier of the client issuing the request. (i.e. submitting the form)
String clientId = getClientId(context); |
With the unique client identifier we can obtain the value of the submitted value contained in the requestMap for this client. We can then set the submittedValue to the value submitted in the form. Note: For this example, we are simply setting the value that was submitted as is, but in other cases we may wish to perform a conversion or validation on the submitted value before setting the value as "valid".
try { String string_submit_val = ((String) requestMap.get(clientId)); setSubmittedValue(string_submit_val); setValid(true); } catch(NumberFormatException ex) { setSubmittedValue((String) requestMap.get(clientId)); } |
That takes care of our new and improved UIComponent rendering (encoding and decoding). I'll leave out the step of registering this component in the faces-config and highlight the remaining trivial code modifications in the tag handler and the TLD.
For the tag handler we have only minor changes. This tag handler as before extends from the same UIComponentTag but will have a bean property of "value" instead of "symbol". The "value" property is the value submitted in the input field and is handled exactly in the same manner before as the symbol. The only other difference from before is that our tag handler is now registered with the StockInput UI Component Class.
public String getComponentType() { return "tss.hello.StockInput"; } |
The setProperties method is basically the same as before but operating on the "value" instead "symbol":
protected void setProperties(UIComponent component) |
Updating the TLD
Similar to before, the new TLD tag entry can be as follows:
... <tag> <attribute> |
Running the Stock Input Component
Invoking this component is done in a similar fashion as before where we simply add the tag to the page:
<sf:stockinput id="stockinput" value="" /> |
Notice that we can leave the value attribute blank but specify it later when we run it. Once the page is running, enter a test stock symbol such as ORCL or AMZN and check out the result!
Summary
As you can see, building custom JSF UI Component is a fairly straightforward process. Once you understand the mechanism detailed in the encode() and decode() methods in the rendering code, the rest is a fairly trivial process of providing the necessary "plumbing" with tag handlers as such to enable usage in a typical client such as a JSP based client.
Future articles on JSF Component development will also build upon these simple examples and show how to build other non visual JSF components such as Validators and Converters.
Biography
Chris Schalk is a Principal Product Manager and Evangelist for Oracle JDeveloper and is responsible for the Web application development features within JDeveloper. Prior to product management, Chris held positions in both software development and technical marketing at Oracle. Before Joining Oracle, he worked at IBM as a software developer. Chris holds a Bachelor's of Science in Applied Mathematics with a specialization in Computer Science from the University of California at Los Angeles.
Chris has written numerous samples and articles for various publications including Javapro, Oracle Magazine and also maintains a popular Blog on J2EE Web development at: http://jroller.com/page/cschalk.