Creating a Custom EL Resolver in JSF

In this tutorial we will create a custom el resolver. We can extend the ELResolver class to create our own implicit variables in JSF. The following are three simple steps to add custom implicit variables.

  1. Create a class that extends from the ELResolver.
  2. Implement the required abstract methods.
  3. Register your custom El resolver with the faces-config.xml.

Custom EL Resolver

By extending the ElResolver class you need to implement 6 methods.

  • getValue: this method returns a value if the base is null and the property requested is named beers.
  • getType: this method identifies the type for our property. Determines wheter the call of the setValue method is safe without causing a ClassCastException to be thrown.
  • setValue: this method tries to set the value for a given property and base. If the variable is readonly you’ll need to throw a PropertyNotWritableException.
  • isReadOnly: determines wheter a variable is readonly.
  • getFeatureDescriptors: this method is used by a design time tool. It returns a set of information about the variables or properties that can be resolved.
  • getCommonPropertyType: returns the most general type that this resolver accepts.
package com.memorynotfound;

import com.sun.faces.util.MessageUtils;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
import java.beans.FeatureDescriptor;
import java.util.Iterator;

public class BeerVarResolver extends ELResolver {

    private static final String BEER_PROPERTY_NAME = "beers";
    private static final String[] BEERS = new String[]{"La Chouffe", "Stella Artois"};
    private final Class<?> CONTENT = String[].class;

    @Override
    public Object getValue(ELContext ctx, Object base, Object property) {
        if ((base == null) && property.equals(BEER_PROPERTY_NAME)){
            ctx.setPropertyResolved(true);
            return BEERS;
        }
        return null;
    }

    @Override
    public Class<?> getType(ELContext ctx, Object base, Object property) {
        if (base != null){
            return null;
        }
        if (property == null){
            String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
            throw new PropertyNotFoundException(message);
        }
        if (property.equals(BEER_PROPERTY_NAME)){
            ctx.setPropertyResolved(true);
            return CONTENT;
        }

        return null;
    }

    @Override
    public void setValue(ELContext ctx, Object base, Object property, Object value) {
        if (base != null){
            return;
        }
        ctx.setPropertyResolved(false);
        if (property == null){
            String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
            throw new PropertyNotFoundException(message);
        }
        if (BEER_PROPERTY_NAME.equals(property)){
            throw new PropertyNotWritableException((String) property);
        }
    }

    @Override
    public boolean isReadOnly(ELContext ctx, Object base, Object property) {
        return true;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext ctx, Object base) {
        return null;
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext ctx, Object base) {
        return String.class;
    }
}

Register Custom El Resolver faces-config.xml

To register our custom el resolver with our JSF application, we need to add the following entry to our faces-config.xml file.

<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                                  http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd" version="2.2">

    <application>
        <el-resolver>com.memorynotfound.BeerVarResolver</el-resolver>
    </application>

</faces-config>

Use Custom EL Resolver

We created our El Resolver and added it to the faces-config.xml. This means we have created an implicit variable that we can use in our JSF pages. We can call the implicit object by #{beers} shown in the example below.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:body>

    <h1>JSF 2.2 Custom El Resolver</h1>

    <ul>
    <c:forEach var="beer" items="#{beers}">
        <li>#{beer}</li>
    </c:forEach>
    </ul>

</h:body>
</html>

Add FacesServlet to the Servlet Descriptor

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">

    <display-name>JavaServerFaces</display-name>

    <!-- Change to "Production" when you are ready to deploy -->
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <!-- Welcome page -->
    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>

    <!-- JSF mapping -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map these files with JSF -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

</web-app>

Demo

jsf custom el resolver example

References

Download

You may also like...