Friday, February 25, 2011

Custom JSF validator for required fields

JSF components implementing EditableValueHolder interface have two attributes "required" and "requiredMessage" - a flag indicating that the user is required to input / select not empty value and a text for validation message. We can use that, but it's not flexible enough, we can't parameterize the message directly in view (facelets or jsp) and we have to do something for a proper message customization. What is about a custom validator attached to any required field? We will write one. At first we need to register such validator in a tag library.
<?xml version="1.0"?>
<facelet-taglib version="2.0" ... >
    <namespace>http://ip.client/ip-jsftoolkit/validator</namespace>
    <tag>
        <tag-name>requiredFieldValidator</tag-name>
        <validator>
            <validator-id>ip.client.jsftoolkit.RequiredFieldValidator</validator-id>
        </validator>
        <attribute>
            <description>Resource bundle name for the required message</description>
            <name>bundle</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <description>Key of the required message in the resource bundle</description>
            <name>key</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <description>Label string for the required message</description>
            <name>label</name>
            <required>false</required>
            <type>java.lang.String</type>
        </attribute>
    </tag>
</facelet-taglib>
We defined three attributes in order to achieve a high flexibility. A simple using would be
<h:outputLabel for="myInput" value="#{text['myinput']}"/>
<h:inputText id="myInput" value="...">
    <jtv:requiredFieldValidator label="#{text['myinput']}"/>
</h:inputText>
The validator class itself is not difficult. Dependent on the "key" parameter (key of the required message) and the "label" parameter (text of the corresponding label) there are four cases how the message gets acquired.
/**
 * Validator for required fields.
 */
@FacesValidator(value = RequiredFieldValidator.VALIDATOR_ID)
public class RequiredFieldValidator implements Validator
{
    /** validator id */
    public static final String VALIDATOR_ID = "ip.client.jsftoolkit.RequiredFieldValidator";

    /** default bundle name */
    public static final String DEFAULT_BUNDLE_NAME = "ip.client.jsftoolkit.validator.message";

    private String bundle;
    private String key;
    private String label;

    @Override
    public void validate(FacesContext facesContext,
        UIComponent component, Object value) throws ValidatorException
    {
        if (!UIInput.isEmpty(value)) {
           return;
        }

        String message;
        String bundleName;

        if (bundle == null) {
           bundleName = DEFAULT_BUNDLE_NAME;
        } else {
           bundleName = bundle;
        }

        if (key == null && label == null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName),
                  "jsftoolkit.validator.emptyMandatoryField.1");
        } else if (key == null && label != null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName),
                  "jsftoolkit.validator.emptyMandatoryField.2", label);
        } else if (key != null && label == null) {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName), key);
        } else {
            message = MessageUtils.getMessageText(
               MessageUtils.getResourceBundle(facesContext, bundleName), key, label);
        }

        throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_WARN, message, StringUtils.EMPTY));
       
        // getter / setter
        ... 
    }
}
MessageUtils is an utility class to get ResourceBundle and message text. We also need two text in the resource bundle (property file)
jsftoolkit.validator.emptyMandatoryField.1=Some required field is not filled in.
jsftoolkit.validator.emptyMandatoryField.2=The required field '{0}' is not filled in.
and the following context parameter in web.xml
<context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
</context-param>
This solution is not ideal because we need to define the label text (like #{text['myinput']}) twice and to attach the validator to each field to be validated. A better and generic validator for multiple fields will be presented in the next post. Stay tuned!

Sunday, February 20, 2011

Invalidate browser cache with custom JSF resource handler

Browser caches static resources like CSS, JavaScript files, images if they don't have proper expire time in the response header. An usual task is to find a solution which allows refetching of resources with each software release. We can modify all resource URLs by appending "&rv=1234" or "?rv=1234" to the end. rv is a current software revision. Such modified URL leads to the refetching of resources. The current revision can be acquired during project build and stored somewhere, e.g. in a text file, in order to make it available in the web application. Maven has e.g. a special plugin for this task called buildnumber-maven-plugin. This plugin allows to get the current revision from a version control system.

We will write a custom JSF resource handler CustomResourceHandler which will accomplish the URL modifying. The resource handler can be registered in faces-config.xml as follows:
<application>
    <resource-handler>packagename.CustomResourceHandler</resource-handler>
</application>
Writing of any resource handler is straightforward. We need to extend javax.faces.application.ResourceHandlerWrapper
public class CustomResourceHandler extends javax.faces.application.ResourceHandlerWrapper
{
    private ResourceHandler wrapped;

    public CustomResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ResourceHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public Resource createResource(String resourceName, String libraryName) {
        Resource resource = super.createResource(resourceName, libraryName);

        // here a check of library name could be necessary, etc.
        ...
        return new CustomResource(resource);
    }
}
We need now a class CustomResource. CustomResource should extend javax.faces.application.ResourceWrapper and delegate all calls to this wrapper class except one call getRequestPath(). Method getRequestPath() needs to be overwritten.
public class CustomResource extends javax.faces.application.ResourceWrapper
{
    private javax.faces.application.Resource resource;

    public CustomResource(Resource resource) {
        this.resource = resource;
    }

    @Override
    public Resource getWrapped() {
        return this.resource;
    }

    @Override
    public String getRequestPath() {
        String requestPath = resource.getRequestPath();
                 
        // get current revision
        String revision = ...

        if(requestPath.contains("?"))
            requestPath = requestPath + "&rv=" + revision;
        else
            requestPath = requestPath + "?rv=" + revision;

        return requestPath;
    }

    @Override
    public String getContentType() {
        return getWrapped().getContentType();
    }

    ...
}
That's all. For more control in Mojarra JSF implementation there are two handy config parameters for web.xml.

com.sun.faces.defaultResourceMaxAge set expire time (in milliseconds) into response header and is responsible for browser-caching. Default value in Production ProjectStage is ca. 10 min.
com.sun.faces. resourceUpdateCheckPeriod gives frequency in minutes to check for changes to webapp artifacts that contain resources.

Thursday, February 17, 2011

Two handy CSS hacks for Internet Explorer

Web developers often face with deficiencies of Internet Explorer. There are many CSS hacks for IE allowing to overcome deficiencies in most cases, but I always use two hacks which are valid for all IE versions. That are * html and \9. Examples:
 
* html .ui-spinner {
    vertical-align: middle;
}

.ui-spinner {
    vertical-align: middle\9;
}
 
Only IE understand them. Other browsers ignore these syntax.

Passing JSF action methods into ui:include

Parameters like Java plain, complex objects and beans can be transfered into an ui:include to be available in the included page. An example:
<ui:include src="/sections/edit.xhtml">
    <ui:param name="editBean" value="#{editTemplate}"/>
    <ui:param name="param1" value="value1"/>
    <ui:param name="param1" value="value1"/>
</ui:include>
The facelets edit.xhtml has then an access to these parameters. Well, but what is about action methods? They can be passed by array notation. We can include a facelets from many other facelets and specify the action by its name. E.g.
<ui:include src="/sections/edit.xhtml">
    <ui:param name="editBean" value="#{editTemplate}"/>
    <ui:param name="deleteAction" value="deleteById"/>
    <ui:param name="obj" value="#{listTemplates.selectedId}"/>
</ui:include>

<ui:include src="/sections/edit.xhtml">
    <ui:param name="editBean" value="#{editTemplate}"/>
    <ui:param name="deleteAction" value="deleteByTemplate"/>
    <ui:param name="obj" value="#{listTemplates.selectedTemplate}"/>
</ui:include>
deleteAction is an action name as String which is defined in the passed bean #{editTemplate}. In the included edit.xhtml we can access the proper action and call it with parameter as follows
 
<h:commandButton value="Delete" action="#{editBean['deleteAction'](obj)}"/> 
 
or if you don't use EL 2.x
 
<h:commandButton value="Delete" action="#{editBean['deleteAction']}">
    <f:setPropertyActionListener target="#{editBean.selectedObject}" value="#{obj}"/>
</h:commandButton> 
 
Like JavaScript objects we can use the array notation in EL expressions.

Wednesday, February 9, 2011

Escape characters in ResponseWriter

While development of JSF components I have faced a task to escape apostrophe ' in the HTTP response generated by javax.faces.context.ResponseWriter. Here is a solution:
ResponseWriter writer = facesContext.getResponseWriter();
...
// swap writers
FastStringWriter fsw = new FastStringWriter();
ResponseWriter clonedWriter = writer.cloneWithWriter(fsw);
facesContext.setResponseWriter(clonedWriter);

// render children
renderChildren(facesContext, uiTreeNode);

// restore the original writer
facesContext.setResponseWriter(writer);

// escape output and write it back
String renderedKids = fsw.toString();
if (renderedKids != null && !renderedKids.isEmpty()) {
    writer.write(renderedKids.replace("'", "\\\'"));
}
...
We replace the original Writer by FastStringWriter, write there, get the output from it, do escaping and write the escaped output to the response.

P.S. This post was triggered by an issue in PrimeFaces. I guess, writeText would do the escaping. But if you have a third-party library and don't have many control over the rendering of component subtrees (s. renderChildren), you have not much alternatives.