Now that we are done with the basics of what a JSF component is, let's see the simplest example of a JSF custom component, the HelloWorld component. The idea of this recipe is to get you familiar with the skeletons of the JSF custom component classes.
We have developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.
We will proceed step by step, and implement each class described earlier in the introduction of the chapter. To begin with, we will write the UI Component class, and for this we keep in mind that the effect of our component is to render a simple message on the client. Knowing that, we can extend the concrete UIOutput
class or, as we did, the UIComponentBase
class (we prefer this class, because we don't need the value
attribute, which is specific for UIOutput
components). Therefore, our component may look as shown next:
package custom.component; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import java.io.IOException; import javax.faces.context.ResponseWriter; public class HelloWorldComponent extends UIComponentBase { public String getFamily() { return "HELLO_WORLD_FAMILY"; } @Override public void encodeBegin(FacesContext ctx) throws IOException { ResponseWriter responseWriter = ctx.getResponseWriter(); String helloworld = (String) getAttributes().get("helloworld"); responseWriter.startElement("b", this); if (helloworld != null) { responseWriter.writeText(helloworld, "helloworld"); } else { responseWriter.writeText("This is a simple Hello World JSF custom component!", null); } responseWriter.endElement("b"); } }
As you can see, we have two methods in HelloWorldComponent
. The getFamily
method, associates a string, representing the component family, with this component. This family is significant, because this value is used to look up the renderer when it is time to make an HTML document.
Since our component only displays a message, and the tag doesn't contain any children, we only need to override the encodeBegin
method, which renders the tag. For advanced components, which have a body, we should have three overridden methods:
encodeBegin
: This starts the element for the root componentencodeChildren
: This would cause all of the children to be encodedencodeEnd
: This closes the elementNext, we will build the tag handler. This class is responsible for creating the component, attaching the renderer to the component, and setting the fields on the component based on the values supplied in JSP. In this case, the tag handler is:
package custom.component; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.webapp.UIComponentELTag; public class HelloWorldComponentTag extends UIComponentELTag { // Mapping helloworld attribute to a bean property public ValueExpression helloworld = null; public String getComponentType() { return "HELLO_WORLD"; } public String getRendererType() { return null; } public ValueExpression getHelloworld() { return helloworld; } public void setHelloworld(ValueExpression helloworld) { this.helloworld = helloworld; } @Override protected void setProperties(UIComponent ui_comp) { super.setProperties(ui_comp); if (!(ui_comp instanceof HelloWorldComponent)) { throw new IllegalStateException("Component " + ui_comp.toString() + " is of wrong type!!!"); } HelloWorldComponent helloWorldComponent = HelloWorldComponent)ui_comp; if (helloworld != null) { helloWorldComponent.setValueExpression("helloworld", helloworld); } } }
Notice that since we don't have a separate renderer class (we don't need one), we return a null
value. The setProperties
method sets the incoming values from the JSP tag by first calling the same method of the parent class along with the custom code to set the value from the helloworld
tag attribute.
Next, we build the TLD document. This file allows us to use our custom JSP tag handler class. The helloworld.tld
file is stored in the WEB-INF
folder of the application (standard J2EE architecture) and it is responsible for associating the tag name to its attributes:
<?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>0.03</tlib-version> <jsp-version>1.2</jsp-version> <short-name>Hello World Component Tag Library</short-name> <uri>http://packt.net/cookbook/components</uri> <description> Custom components tag library. </description> <tag> <name>helloWorldUI</name> <tag-class>custom.component.HelloWorldComponentTag</tag-class> <body-content>empty</body-content> <description> This custom component says hello. </description> <attribute> <name>helloworld</name> <required>false</required> <deferred-value> <type>java.lang.Object</type> </deferred-value> <description> The attribute that will contain the hello message. </description> </attribute> <attribute> <name>id</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> The component identifier for this component. </description> </attribute> <attribute> <name>immediate</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> Immediate conversion and validation. </description> </attribute> <attribute> <name>rendered</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> Indicates if the component should be rendered or processed on any subsequent form submit. </description> </attribute> <attribute> HelloWorld componentbuilding<name>required</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> Flag indicating that the user is required to provide a submitted value for this input component. </description> </attribute> <attribute> <name>validator</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> A validator method that will be called to perform validation. </description> </attribute> <attribute> <name>binding</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description> A value binding that points to a bean property. </description> </attribute> </tag> </taglib>
Now, the HelloWorld custom component is done! The last thing that has to be done is to add the corresponding lines in the faces-config.xml
descriptor (this is necessary even if we are using JSF 2.0). These lines will configure the component as follows:
<?xml version='1.0' encoding='UTF-8'?> <faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"> <component> <component-type>HELLO_WORLD</component-type> <component-class> custom.component.HelloWorldComponent </component-class> </component> </faces-config>
Time to see what we have done! For this you can call our component from a JSP page as shown next:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <%@taglib prefix="e" uri="http://packt.net/cookbook/components"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <f:view> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Writing a simple JSF custom component an hello world component</title> </head> <body> <e:helloWorldUI helloworld="Hello from Packt!"/> </body> </html> </f:view>
Notice that we have added the component taglib
element right after the JSF/HTML taglib
element.
The output will be the following message:
Hello from Packt