Renderers/validators for custom components

Based on knowledge from the previous recipe, we will move forward and create a custom component that will be rendered by a custom renderer and will have attached a custom validator (as an exercise, try to add a custom converter as well). Our component will consist of a text field that accepts only valid e-mail addresses; therefore it will extend the UIInput component.

Getting ready

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.

How to do it...

To begin with, let's say that the new component will be named emailInput and it looks as shown next (we have listed the entire JSP page):

<%@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 JSF custom component
- an email component</title>
</head>
<body>
<h:form id="emailForm">
<h:outputText value="Insert your e-mail:"/><br />
<e:emailInput value="#{myEmailBean.email}" id="emailID" />
<h:message showSummary="true" showDetail="false"
for="emailID" style="color: red;
text-decoration:overline"/>
<h:commandButton id="submit"
action="response?faces-redirect=true" value="Submit"/>
</h:form>
</body>
</html>
</f:view>

As usual, we start by developing the component class (the component class controls the server-side behavior of a JSF component). This class is listed next:

package custom.component;
import javax.faces.component.UIInput;
public class UIEmailInput extends UIInput {
public UIEmailInput() {
super();
EmailValidator emailValidator=new EmailValidator();
addValidator(emailValidator);
}
@Override
public String getFamily() {
return "EMAIL_FAMILY";
}
}

As you can see, we have used the component constructor for setting the custom validator, EmailValidator, which is listed next (for more details about writing validators refer to Chapter 2, Using Standard and Custom Validators in JSF):

package custom.component;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
@FacesValidator(value = "emailValidator")
public class EmailValidator implements Validator {
private static final String IP_REGEX = ".+@.+\.[a-z]+";
public void validate(FacesContext context,
UIComponent component, Object value) throws ValidatorException {
String emailAddress = (String) value;
Pattern mask = null;
mask = Pattern.compile(IP_REGEX);
Matcher matcher = mask.matcher(emailAddress);
if (!matcher.matches()) {
FacesMessage message = new FacesMessage();
message.setDetail("E-mail not valid");
message.setSummary("E-mail not valid");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}

The next task is to write a custom renderer. This class will be responsible for transforming the component into HTML and taking any form posts and passing the values from the post back to the component. We will first list the code, and then look into the details:

package custom.component;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class UIEmailInputRenderer extends Renderer{
@Override
public void decode(FacesContext ctx, UIComponent ui_comp) {
if (ctx == null) {
throw new NullPointerException("NULL CONTEXT NOT ALLOWED!");
} else if (ui_comp == null) {
throw new NullPointerException("NULL COMPONENT NOT ALLOWED!");
}
if (ui_comp instanceof UIInput) {
UIInput uiInput = (UIInput)ui_comp;
String clientId = uiInput.getClientId(ctx);
Map requestMap = ctx.getExternalContext().
getRequestParameterMap();
String new_value = (String)requestMap.get(clientId);
if (null != new_value) {
uiInput.setSubmittedValue(new_value);
}
}
}
@Override
public void encodeEnd(FacesContext ctx, UIComponent ui_comp) throws
IOException {
if (ctx == null) {
throw new NullPointerException("NULL CONTEXT NOT ALLOWED!");
} else if (ui_comp == null) {
throw new NullPointerException("NULL COMPONENT NOT ALLOWED!");
}
ResponseWriter responseWriter = ctx.getResponseWriter();
responseWriter.startElement("input", ui_comp);
responseWriter.writeAttribute("type", "text", "text");
String id = (String)ui_comp.getClientId(ctx);
responseWriter.writeAttribute("id", id, "id");
responseWriter.writeAttribute("name", id, "id");
Object obj = getValue(ui_comp);
responseWriter.writeAttribute("value",
formattingValue(obj), "value");
responseWriter.endElement("input");
}
private String formattingValue(Object format_value) {
return format_value.toString();
}
protected Object getValue(UIComponent ui_comp) {
Object obj = null;
if (ui_comp instanceof UIInput) {
obj = ((UIInput) ui_comp).getSubmittedValue();
}
if ((null == obj) && (ui_comp instanceof ValueHolder)) {
obj = ((ValueHolder) ui_comp).getValue();
}
return obj;
}
}

The first method, named decode, takes parameters from a form post and sets the values for the component. After checking the context and component state (they can't be null), we isolate the UIInput components and we extract values from the request and put them as submitted values for the component.

The next method is encodeEnd. It generates the HTML code to represent the component on the browser. For advanced components, which have a body, we should have three overridden methods (we won't repeat this again, therefore it is considered known in the following recipes):

  • encodeBegin: This starts the element for the root component
  • encodeChildren: This would cause all of the children to be encoded
  • encodeEnd: This closes the element

Now, the tag handler should indicate that we have a separate renderer class, and for this the getRendererType method must not return null:

public String getRendererType() {
return "EMAIL_RENDERER";
}

The last thing that we must accomplish is to set the renderer in the faces-config.xml descriptor (we need this even if we are using JSF 2.0). This can be done as shown next:

…
<render-kit>
<renderer>
<description>
Renderer for the e-mail component.
</description>
<component-family>EMAIL_FAMILY</component-family>
<renderer-type>EMAIL_RENDERER</renderer-type>
<renderer-class>
custom.component.UIEmailInputRenderer
</renderer-class>
</renderer>
</render-kit>
…

That's all! Now, we have a custom component that can be used for providing valid e-mail addresses to our bean, MyEmailBean.

Note

This recipe only presents an example of a custom component with a custom validator attached and a renderer class. Don't think from this that validators and renderer are somehow related, because they aren't!

How it works...

If you read the introduction of this chapter it becomes easier to understand how our custom component works.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: Renderers_and_validators_custom_component

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset