-
Ali Noor
What is JSF
JSF is a server side UI Component based framework for java based web applications. It adapts Component centric approach to develop java web user interfaces, hence simplifying the development. JSF follows the MVC design pattern and creates the manageable separation between the view, application data and logic.
Since the true power of JSF lies in the UI component model and JSF allows us to use prebuilt components or we can build our own, depending on our requirements.
Let’s start with creating a custom component, which would also help us understand who the compoenent model works in jsf.
Writing a Custom JSF Component
Lets start with a simple component which takes the email address as an input.
A component is made up of three main classes
o UIComponent subclass.
o Renderer
o UIComponentTag subclass
First of all we start building a UIComponent subclass, since our component is and input component we would extend this class by javax.faces.component.UIInput.
Here is how our class looks like
public class EmailComponent extends UIInput {
public static final String
EMAIL_FAMILY = “EMAILFAMILY”;
public EmailComponent() {
super();
addValidator(new EmailValidator());
}
public String getFamily() {
return EMAIL_FAMILY;
}
}
The only thing that needs explanation in this class is getFamily() method, this method is used to get the component family which is used to look up the renderer for this component when HTML is to be generated for this component.
addValidator(); is used to make sure that the user enters a valid email address. The EmailValidator class looks like this,
public class EmailValidator implements Validator {
public void validate(FacesContext context,
UIComponent component,
Object value)
throws ValidatorException {
if (null != value) {
if (!(value instanceof String)) {
throw new IllegalArgumentException(
“The value must be a String”);
}
String email = (String) value;
Pattern emailPattern = Pattern
.compile(“[a-z0-9!#$%&'*+/=?^_`{|}~-]+” +
“(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)” +
“*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)” +
“+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?”);
Matcher emailMatcher = emailPattern
.matcher(email);
if (!emailMatcher.matches()) {
throw new ValidatorException(
new FacesMessage(
“Please enter a valid Email Address”));
}
}
}
}
The important thing to know in this validator is if the string does not match the Matcher, then a new FacesMessage is created and a ValidatorException is thrown. JSF will interpret the ValidationException and get the message from it.
Another important thing is, this validator is not part of building a custom component.
We have created a UIComponent Subclass, now next step is to create a renderer, renderer is responsible for turning the component into HTML and taking posted HTML and turning it into values suitable for the component, these processes are also referred as encoding and decoding.
Below is the code for the renderer, I think the comments on the code are explaining it well.
public class EmailRenderer extends Renderer {
//THIS METHOD IS RESPONSIBLE FOR TAKING ANY PARAMETERS THAT WERE PASSED
//IN FROM A FORM POST AND SETTING THE VALUE ON THE COMPONENT
public void decode(FacesContext context,
UIComponent component) {
assertValidInput(context, component);
if (component instanceof UIInput) {
UIInput input = (UIInput) component;
String clientId = input
.getClientId(context);
Map requestMap = context
.getExternalContext()
.getRequestParameterMap();
String newValue = (String) requestMap
.get(clientId);
if (null != newValue) {
input.setSubmittedValue(newValue);
}
}
}
//THIS METHOD IS RESPONSIBLE FOR BUILDING THE HTML TO REPRESENT //THE COMPONENT IN THE BROWSER
//BASICALLY THERE ARE THREE ENCODING METHODS ENCODEBEGIN(), //ENCODECHILDREN() AND ENCODEEND().
//SINCE OUR COMPONENT DOESN’T HAVE ANY CHILDREN WE DON’T NEED //PREVIOUS TWO METHODS.
public void encodeEnd(FacesContext ctx,
UIComponent component) throws IOException {
assertValidInput(ctx, component);
ResponseWriter writer = ctx
.getResponseWriter();
writer.startElement(“input”, component);
writer.writeAttribute(“type”, “text”, “text”);
String id = (String) component
.getClientId(ctx);
writer.writeAttribute(“id”, id, “id”);
writer.writeAttribute(“name”, id, “id”);
String size = (String) component
.getAttributes().get(“size”);
if (null != size) {
writer.writeAttribute(“size”, size, “size”);
}
Object currentValue = getValue(component);
writer.writeAttribute(“value”,
formatValue(currentValue), “value”);
writer.endElement(“input”);
}
protected Object getValue(UIComponent component) {
Object value = null;
if (component instanceof UIInput) {
value = ((UIInput) component)
.getSubmittedValue();
}
// if its not a UIInput or the submitted value
// was null then get the value (it should
// always be a UIInput)
if (null == value
&& component instanceof ValueHolder) {
value = ((ValueHolder) component)
.getValue();
}
return value;
}
private String formatValue(Object currentValue) {
//THIS METHOD SHOULD PERFORM THE CONVERSION NEEDED.IN OUR //CASE WE JUST NEED A STRING.
return currentValue.toString();
}
private void assertValidInput(
FacesContext context, UIComponent component) {
if (context == null) {
throw new NullPointerException(
“context should not be null”);
} else if (component == null) {
throw new NullPointerException(
“component should not be null”);
}
}
}
We are done with the first two classes, now the last class is the subclass of UIComponentTag.
Below is the code for this class
public class EmailInputTag extends UIComponentTag {
private String size;
private String value;
private static final String EMAIL_COMP_TYPE =
“EMAIL_INPUT”;
private static final String EMAIL_RENDER_TYPE =
“EMAIL_RENDERER”;
@Override
public String getComponentType() {
return EMAIL_COMP_TYPE;
}
@Override
public String getRendererType() {
return EMAIL_RENDER_TYPE;
}
protected void setProperties(
UIComponent component) {
FacesContext context = FacesContext
.getCurrentInstance();
super.setProperties(component);
if (null != size) {
component.getAttributes().put(“size”, size);
}
if (null != value) {
if (isValueReference(value)) {
ValueExpression ve= context.getApplication().getExpressionFactory().createValueExpression
(context.getELContext(),value,String.class);component.setValueExpression(“value”, ve);
} else {
((UIInput) component).setValue(value);
}
}
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Two important methods here, getComponentType() and getRendererType().
getComponentType() returns the type of the component that should be created by the tag,this type is sent to the factory for the resolution of the class name and then the instance is created,
getRendererType() returns the type of the renderer and the instance is created the same way as mentioned above. These two methods are responsible for making the connection between the renderer and the component.
Now we are done with all the coding now we have to do some configuration, in faces-config.xml file we have to register our custom component with its renderer, like this.
<component>
<component-type>
EMAIL_INPUT
</component-type>
<component-class>
com.zigron.component.EmailComponent
</component-class>
</component>
Note the <component-type> tag, this is the component type we defined in com.zigron.tag.EmailInputTag class.
And the renderer like this,
<render-kit>
<renderer>
<description>
Renderer for the Email Input component.
</description>
<component-family>EMAILFAMILY</component-family>
<renderer-type>
EMAIL_RENDERER
</renderer-type>
<renderer-class>
com.zigron.renderer.EmailRenderer
</renderer-class>
</renderer>
</render-kit>
One more missing piece is a the tld that would define the tag for JSP, I would paste a part of the email.tld here.
<tlib-version>0.01</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>Email Input Example Component Tag Library</short-name>
<uri>http://www.zigron.com/jsf/component/email</uri>
<description>
This tag library has the tags for the custom jsf component, which is just a simplest example, hence most of the attributes are ignored in this tld
</description>
<tag>
<name>emailInput</name>
<tag-class>com.zigron.tag.EmailInputTag</tag-class>
<body-content>empty</body-content>
<description>
This is the tag for the Email input component.
</description>
<attribute>….</attribute>
…….
</tag>
Now we have to use this tag on JSP, for that we need to import our taglib, with the URI we mentioned in our email.tld, i.e http://www.zigron.com/jsf/component/email
Here is the example JSP code,
<%@taglib prefix=“h”
uri=“http://java.sun.com/jsf/html”%>
<%@taglib prefix=“f”
uri=“http://java.sun.com/jsf/core”%>
<%@taglib prefix=“email”
uri=“http://www.zigron.com/jsf/component/email”%>
<html>
<head>
<title>Custom Email Component</title>
</head>
<body>
<f:view>
<p>
<h:messages id=“messageList” showSummary=“true”/>
</p>
<h:form>
<h:outputLabel for=“test”>
<h:outputText id=“testLabel” value=“Email Address:”/>
</h:outputLabel>
<email:emailInput id=“test” size=“19″
value=“#{test.emailAddress}“/>
<br/>
<h:commandButton value=“Save”
action=“#{test.showEmailAddress}“/>
</h:form>
</f:view>
</body>
</html>
In this jsp test is the managed bean registered in faces-config.xml.
Now by combining all the above pieces together our component is now ready to serve.
Just for an over view , here is the step by step explanation
1. The URI prefix is used to find the uri in the tld, (email is the prefix in our example)
2. From the TLD the tag name and class pointing to that tag, under the URI is located, in our case tag name is <name>emailInput</name> in tld and the tag class is <tag-class>com.zigron.tag.EmailInputTag</tag-class>.
3. The getComponentType() method of located tag class(com.zigron.tag.EmailInputTag) is called.
4. The returned value is located in the faces-config.xml file to find out the component class against that component type.( as I mentioned earlier as a Note.)
5. Finally the component class is located from the faces-config.xml.
Download Complete Document Here


