EL expressions are one of the main driving forces for JavaServer Faces. Most dynamic characteristics of pages and widgets are governed by EL expressions. In JSF 1.x, there are some limitations for EL expressions that can at times be a little frustrating. One of the limitations is the fact that no custom functions or operators can be used in EL expressions. Quite some time ago, I wrote this article – https://technology.amis.nl/blog/622/how-to-call-methods-from-el-expressions-pre-jsp-20-trick-for-jsps-with-jstl – to demonstrate a trick for using a Map interface implementation to access custom functionality from EL expression after all.
However, things can even be better. Rather than jumping through the somewhat elaborate hoops of implementing the Map and consructing complex EL expressions, there are two other approaches. One is to create a custom EL Resolver can configure it in the faces-config.xml. Another is discussed in this article. It involves registering custom Java methods as eligible for use in EL expressions. And that really makes life a lot easier. It allows us to create EL expressions such as:
#{cel:concat (cel:upper( bean.property), cel:max(bean2.property, bean3.property), cel:avg(bean4.list))}
or
#{cel:substr(bean.property, 1, 5)}
Leveraging new custom operators in EL expressions is done in a few simple steps:
- Create custom class with static method(s)
- Create a tag library (.tld file)
- Register each method that should be supported in EL expressions
- Add a reference to the tag library’s URI in the jsp:root element for the page
- Use the registered functions in EL expressions in the page
As a very simple example, let’s take a look at two EL extensions: a concat operator and an upper.
1. Create custom class with static method(s)
The class could hardly be simpler:
package nl.amis.jsf; public final class ELFun { /** * Method that concattenates two strings. More strings can be concattenated * through nested calls such as * #{cel:concat('a', cel:concat('b','c'))} * * @param first string to concattenate * @param second string to concattenate * @return first and second string concattenated together */ public static String concat(final String first, final String second) { return (first == null ? "" : first) + (second== null ? "" : second); } /** * Function that returns the uppercased rendition of the input. * * to be used in EL expressions like this: * #{cel:upper('a')} * can be combined with other functions like this: * #{cel:concat( cel:upper('a'), cel:upper('B'))} * * @param input string to uppercase * @return input turned to uppercase */ public static String upper(final String input) { return (input== null ? "" : input.toUpperCase()); } }
2. Create a tag library descriptor (.tld file)
A TLD file is a straightforward XML document, used to descripe custom JSF UI components, Validators and other extension. And also custom functions. A TLD file is typically located in the WEB-INF directory of the application. An important element of the TLD is the uri. This element is used to identify the Tag Library when referenced from pages.
<?xml version = '1.0' encoding = 'windows-1252'?> <taglib 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-jsptaglibrary_2_1.xsd" version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"> <display-name>customELfun</display-name> <tlib-version>1.0</tlib-version> <short-name>customELfun</short-name> <uri>/nl.amis/customELfun</uri> </taglib>
3. Register each method that should be supported in EL expressions
The TLD file contains a function entry for each operator to be enabled for use in EL expressions. For a function we need to indicate the name to be used in EL expressions, a reference to the class that contains the implementation for the function and the exact signature – name, result type and input parameters – for the method that is backing the function:
<?xml version = '1.0' encoding = 'windows-1252'?> <taglib 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-jsptaglibrary_2_1.xsd" version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"> <display-name>customELfun</display-name> <tlib-version>1.0</tlib-version> <short-name>customELfun</short-name> <uri>/nl.amis/customELfun</uri> <function> <name>concat</name> <function-class>nl.amis.jsf.ELFun</function-class> <function-signature>java.lang.String concat(java.lang.String, java.lang.String)</function-signature> </function> <function> <name>upper</name> <function-class>nl.amis.jsf.ELFun</function-class> <function-signature>java.lang.String upper(java.lang.String)</function-signature> </function> </taglib>
4. Add a reference to the tag library’s URI in the jsp:root element for the page
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:cel="/nl.amis/customELfun">
5. Use the registered functions in EL expressions in the page
<h:form id="f1"> <h:outputText id="ot" value="#{cel:concat('hello',cel:concat(' ', cel:upper('world')))}" /> </h:form>
And that really is all there is to it. You could choose to create the Tag Library and custom class(es) in a separate project, deploy it to JAR and associate the JAR file with other JSF projects that then can leverage these custom functions in their EL expressions.
(Note: my thanks goes to Robert Willem of Brilman who first introduced me to this functionality)
Thanks for sharing. How is it different in JSF 2.0? The only step that is missing is referring .tld in web.xml as facelets tag library.