Developer: J2EE & Web Services

Using the JSP 2.0 EL API
by Andrei Cioroianu

Learn how to evaluate JSP expressions dynamically, use the Expression Language (EL) in XML configuration files, and optimize EL usage when presenting SQL result sets

Download source code for this article

The JSP 2.0 Expression Language (EL) is a much-needed feature for Web developers who want to build scriptless JSP pages. It was designed as a part of the JSP Standard Tag Library (JSTL) 1.0, and then JavaServer Pages (JSP) 2.0 adopted and enhanced the EL.

In its current form, the EL defines an easy-to-use syntax for accessing JavaBean properties, Java collections, scoped attributes, initialization and request parameters, HTTP headers, and cookies without using Java scriptlets in JSP pages. This makes the code more readable and improves the maintainability of the Web pages. In addition, the EL provides a full set of operators that let you build arithmetic, logical, and conditional expressions. JSP 2.0 added a new feature called EL functions that can be used to call static Java methods from Web pages without using Java code. The full power of the JSP EL is exposed through a simple application programming interface (API) to Java programmers, allowing them to use the EL in unconventional ways. For example, the customizability of a Web application can be improved by using the EL within the web.xml configuration file. The EL API is needed for evaluating the expressions from the XML file.

This article presents the EL API, using it in several utility classes whose static methods are called from JSP pages using EL functions. The article also shows several practical uses of the EL API in JSP pages and in custom tag handlers based on the Simple Tags API, which is another new feature of JSP 2.0. One of the examples demonstrates how the JSP EL can be used in XML configuration files.

A previous Oracle Technology Network (OTN) article of mine—"Creating JSP 2.0 Tag Files"—contains a complete set of JSP pages and tag files that use the EL, JSTL, and SQL to create a table; query it; and insert, update, and delete rows. The page that queries the database uses the EL to present the result set. That page is optimized in this article by parsing the expressions once and evaluating them multiple times, in a loop, with the help of the EL API.

Expression Language API Overview

The javax.servlet.jsp.el API consists of two classes (Expression and ExpressionEvaluator), two interfaces (VariableResolver and FunctionMapper), and two exceptions (ELException and ELParseException). Each of these classes and interfaces has only one or two methods. Despite its simplicity, the EL API provides everything you need in order to use the Expression Language outside of the JSP pages. The following instructions describe what you have to do in order to evaluate an expression in your Java code, using the EL API.

Step 1: Get an ExpressionEvaluator. If you develop a Java tag handler, call the getExpressionEvaluator() method of the object returned by getJspContext(). In tag files and JSP pages, you can call the method with the same name provided by the jspContext and pageContext implicit objects.

Step 2: Get a VariableResolver. Call the getVariableResolver() method of the JSP context. This method returns an object that provides access to the JSP variables and implicit objects. You may also develop your own VariableResolver implementation, if necessary.

Step 3 (optional): Provide a FunctionMapper. If you want to use EL functions in your expressions, you have to implement the FunctionMapper interface. The evaluate() method of the EL API accepts a null function mapper. Therefore, this parameter is optional.

Step 4: Evaluate the Expression. Call the evaluate() method of the expression evaluator, passing the following parameters: the expression (as a String), its expected type (as a Class), the variable resolver, and a function mapper that may be null. The evaluate() method returns the expression's value that will have the expected type or a subclass of it. If you don't know what type to expect, you may specify Object.class.

Building a utility class. Our ELUtils class (see source code) provides utility methods that can reduce the usage of the EL API to a single line of code. The ELUtils.evaluate() methods perform the first, second, and fourth steps described above. These methods use their JspContext parameter to get an expression evaluator and a variable resolver, which are used to evaluate the given expression whose value is returned. We'll implement the function mapper of the optional third step for another example of this article.

When you invoke the evaluate() method of the expression evaluator, the JSP container of the application server parses the expression, gets the values of the variables, calls the EL functions, applies the operators, and obtains a value, which is converted to the expected type. Note that evaluate() may throw an ELException if the expression is syntactically incorrect or if an error is generated by a type conversion, by an invalid array index, by a bean method throwing an exception, or by something else.

A later section of this article shows how to call the methods of the ELUtils class from a JSP page using EL functions. In order to simplify the functions' usage, the expectedType parameter may be either a String or a Class instance. If it's a String, the getClass() method of ELUtils gets the Class object for the given name using Class.forName().

Using the EL API in Custom Tag Handlers

As explained in the previous section, the EL API requires a JspContext in order to evaluate expressions. Such a context object exists in every JSP page and is transmitted to the Java classes that handle the custom tags used within the page. Therefore, custom tag handlers are the perfect place for using the EL API.

Adding EL support to the Simple Tags API. The javax.servlet.jsp.tagext package contains the APIs that allow you to build tag handlers. Most of these classes are inherited from JSP 1.x, and they are used to build the so-called Classic Tags. JSTL is one of the many tag libraries that uses the Classic Tags API, which include the Tag, BodyTag, IterationTag, and TryCatchFinally interfaces, as well as the TagSupport and BodyTagSupport classes. The javax.servlet.jsp.tagext package also contains the SimpleTag interface and the SimpleTagSupport class, which form the Simple Tags API. This is a new API introduced in JSP 2.0 as a replacement for the older JSP 1.x classes and interfaces. Our ELTagSupport class extends SimpleTagSupport with a convenience method that takes a JSP expression and an expected type, passing them to ELUtils.evaluate() together with the JspContext object returned by the getJspContext() method inherited from SimpleTagSupport:

public class ELTagSupport extends SimpleTagSupport {
    protected Object evaluate(String expression,
            Object expectedType) throws JspException {
        return ELUtils.evaluate(
            expression, expectedType, getJspContext());
    }
    ...
}

Solving an API limitation problem. From the Web developer's perspective, all custom JSP tags look the same regardless if they are based on the Simple Tags API or on the Classic Tags API. The new JSP 2.0 API was designed for Java developers who were complaining that the old API was unnecessarily complex. The Simple Tags API is very easy to use, but it has one limitation: the JspContext class does not have methods for obtaining the JSP implicit objects such as request, response, session, and application. Theoretically, this limitation creates the opportunity to use the Simple Tags API outside of the Servlet/JSP environment. In practice, however, all custom tags are used in JSP pages, and many of them need access to the JSP implicit objects.

Fortunately, in the case of many JSP containers, including Oracle Application Server Containers for J2EE (OC4J) 10g, you can cast the JspContext to PageContext, which lets you obtain the JSP implicit objects. This procedure is very efficient, but it might not work with every application server. For those JSP containers that don't support the cast to PageContext, you could use the EL API, which should always work in a Servlet/JSP environment, even if this second solution is less efficient. The ELTagSupport class shows how to combine the two procedures to make sure that your code works with every J2EE application server and is as efficient as possible.

The getRequest(), getResponse(), getSession(), and getApplication() methods of our ELTagSupport class try to cast the JspContext object to PageContext in order to obtain the JSP implicit objects with the methods provided by the PageContext class. If this is not possible, ELTagSupport queries the pageContext implicit object with the expression language. In a non-Servlet environment, our methods would return null. The following table contains the methods that are called and the expressions that could be evaluated to get the JSP implicit objects from the first column. The Java classes of those objects are indicated in the second column.

JSP ObjectJava ClassPageContext Method and JSP Expression
requestHttpServletRequestPageContext.getRequest() ${pageContext.request}
responseHttpServletResponsePageContext.getResponse() ${pageContext.response}
sessionHttpSessionPageContext.getSession() ${pageContext.session}
applicationServletContextPageContext.getServletContext() ${pageContext.servletContext}

Expression Language Functions

EL functions allow you to call static Java methods within your EL expressions with the following syntax:

libraryPrefix:functionName(param1, param2, ...)

Functions are defined in JSP libraries that may also contain custom tags. The used libraries must be declared in the JSP page with the <%@taglib%> directive:

<%@taglib prefix="libraryPrefix" 
          uri="/technology/WEB-INF/.../libraryDescriptor.tld" %>

or

<%@taglib prefix="libraryPrefix" 
          uri="http://.../libraryDescriptor.tld" %>

Defining EL functions. The mapping between an EL function and a static Java method must be defined in a .tld file. For example, the first evaluate() method of ELUtils is mapped to an EL function, in our el.tld library descriptor (see source code), with the following declarations:

<function>
    <name>evaluate</name>
    <function-class>jsputils.el.ELUtils</function-class>
    <function-signature>
        java.lang.Object evaluate(
            java.lang.String, java.lang.Object,
            javax.servlet.jsp.JspContext)
    </function-signature>
</function>

The el.tld file defines similar mappings for all static methods of the ELUtils, FNMapper, and PEWrapper classes in our source code. Because the name of each EL function must be unique in a .tld file, we use evaluate2() and evaluate3() for the second evaluate() method of ELUtils and for the method with the same name provided by PEWrapper.

Using EL functions. The ELTest.jsp page contains a form with a single input field, in which users may type an EL expression. When they click the "Evaluate" button, the JSP page receives the expression, which is evaluated with the function defined above. The value returned by el:evaluate() is stored in a JSP variable (exprValue) with the <c:set> tag of JSTL:

<c:set var="exprValue"
    value="${el:evaluate(param.expr,
        'java.lang.Object', pageContext)}"/>

Then, the expression and its value are printed with the <c:out> tag that performs any necessary HTML encoding (replacing < with <, > with >, and so on):

<c:out value="${param.expr}"/> =
<c:out value="${exprValue}"/> <br>

If you have just learned the EL, you may use ELTest.jsp below to verify the syntax of your expressions and to see what results they produce. Don't forget to wrap the EL constructs with ${ and }. Note that an EL expression may contain multiple ${...}, whose values are concatenated.

<%@ taglib prefix="el" uri="/technology/WEB-INF/el.tld" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<body>
<form method="post">

<c:if test="${!empty param.expr}">
    <c:set var="exprValue"
        value="${el:evaluate(param.expr,
            'java.lang.Object', pageContext)}"/>
    <c:out value="${param.expr}"/> =
    <c:out value="${exprValue}"/> <br>
</c:if>

<input type="text" name="expr" size="40"
    value="<c:out value='${param.expr}'/>">
<input type="submit" value="Evaluate">

<c:if test="${empty param.expr}">
    <br> Example: \${ 1 + 2 }
</c:if>

</form>
</body>
</html>

Implementing Function Mappers

Function mappers are needed when you want to use EL functions within the expressions that are evaluated with the EL API. The FunctionMapper interface has only one method, named resolveFunction(), which takes two parameters (a library prefix and a function name) and must return the java.lang.reflect.Method object that provides information about, and access to, the static Java method that is mapped to the EL function with the given name.

Our FNMapper class keeps the function-method mappings in a java.util.HashMap. The resolveFunction() method uses the prefix and localName parameters to build a key that is passed to the get() method of the java.util.HashMap object, which returns the corresponding Method instance:

public class FNMapper implements FunctionMapper {
    private HashMap functionMap;
    ...

    public Method resolveFunction(
            String prefix, String localName) {
        return (Method) functionMap.get(prefix+':'+localName);
    }

}

Building the function map. The buildMap() method of FNMapper builds the function map using the Java Reflection API. Each public static method of the given class is mapped to an EL function with the same name. This works fine as long as the class doesn't have multiple static methods with the same name.

private void buildMap(String prefix, Class clazz) {
    Method methods[] = clazz.getMethods();
    for (int i = 0; i < methods.length; i++) {
        Method m = methods[i];
        if (Modifier.isStatic(m.getModifiers()))
            functionMap.put(prefix+':'+m.getName(), m);
    }
}

The buildMap() method is called by the FNMapper() constructor that gets the Class instance with Class.forName(). The constructor is declared private because the FNMapper class manages its own instances. The public getInstance() method takes an id parameter and returns the requested function mapper. In its current implementation, FNMapper supports only the JSTL function library, but you could easily modify it to support others. Note that the same function mapper instance could be used for multiple function libraries that have different prefixes. This is actually necessary if you want to use functions from different libraries within the same expression.

Our FNMapper works only with the Apache implementation of JSTL 1.1 because the class name is hardcoded. A more general function mapper would have to parse a .tld file, extracting the name of the class containing the static methods. The names of the EL functions and the mapping information should also be obtained from the .tld file. However, the simple FNMapper class is sufficient for testing and learning purposes.

Testing the function mapper. The FNTest.jsp page is similar to ELTest.jsp. In order to enable the support for the JSTL functions, FNTest.jsp uses el:evaluate2(), passing a function mapper obtained with el:getFNMapper('fn'):

<c:set var="exprValue"
    value="${el:evaluate2(param.expr, 'java.lang.Object',
        pageContext, el:getFNMapper('fn'))}"/>

The evaluate2() function is mapped to the second evaluate() method of ELUtils, which accepts the function mapper parameter. The getFNMapper() function is mapped to the FNMapper.getInstance() method in the el.tld file.

Note that FNTest.jsp doesn't have to declare the JSTL function library with <%@taglib%> because the EL expressions typed by users are evaluated in the Java code of ELUtils with the EL API, and because our own FNMapper class does the JSTL function-method mapping.

Using the EL in XML Configuration Files

In "Creating JSP 2.0 Tag Files," I demonstrated how to update and query a database using JSP, JSTL, and SQL. In the remainder of this article, we'll use the JSP 2.0 EL API to improve some of the JSP examples of the previous article.

Modifying the Web application descriptor (web.xml). In "Creating JSP 2.0 Tag Files," the name of a datasource (jdbc/dbtags) was provided in the web.xml configuration file and was obtained by a tag file fragment (init.tagf) with ${initParam.tags_db_dataSource}. We now have an additional initialization parameter named debug_mode, which indicates whether the application runs in a testing or a production environment:

<context-param>
    <param-name>debug_mode</param-name>
    <param-value>true</param-value>
</context-param>
 

Suppose that, depending on the value of the debug_mode flag, we want to choose one of two databases that have identical structures. This choice could be hardcoded in our JSP pages, but it can also be specified in the web.xml file using the JSP EL:

<context-param>
    <param-name>tags_db_dataSource</param-name>
    <param-value>jdbc/${
        initParam.debug_mode ? "dbtags" : "production"
    }</param-value>
</context-param>

After this configuration change, ${initParam.tags_db_dataSource} returns an expression that must be evaluated with the EL API.

Getting the datasource name. The XMLConfig.jsp page obtains the datasource name using the el:evaluate() function, which returns jdbc/dbtags or jdbc/production, depending on the value of debug_mode. The datasource name is stored into a JSP variable (evaluated_tags_db_dataSource) with <c:set>:

<c:set var="evaluated_tags_db_dataSource"
    value="${el:evaluate(initParam.tags_db_dataSource,
        'java.lang.String', pageContext)}"/>

The XMLConfig.jsp page outputs the debug_mode parameter, the expression, and its value with the following code:

debug_mode: ${initParam.debug_mode} <br>
expression: ${initParam.tags_db_dataSource} <br>
value: ${evaluated_tags_db_dataSource}

Here is the resulting output:

debug_mode: true 
expression: jdbc/${ initParam.debug_mode ? "dbtags" : "production" } 
value: jdbc/dbtags

The init.tagf file obtains the datasource name like XMLConfig.jsp, but instead of outputting some information, init.tagf creates a javax.sql.DataSource variable with the <sql:setDataSource> tag of JSTL:

<sql:setDataSource
    dataSource="${evaluated_tags_db_dataSource}"
    var="tags_db_dataSource" scope="application"/>

The init.tagf fragment is included within the select.tag file, which is presented in the next section of this article.

EL Optimizations with Parsed Expressions

Before evaluating an expression, the JSP container has to parse it in order to verify its syntax and to obtain information about the used variables, functions, operators, and so on. This process may involve many string operations and may create lots of temporary objects, which have to be deleted from memory later by the JVM's garbage collector. When using an expression within a loop of a JSP page, tag file, or tag handler, it makes sense to parse the expression only once, outside of the loop, and evaluate the parsed expression within the loop as many times as necessary. The following instructions describe how to accomplish this optimization with the EL API.

Step 1: Get an ExpressionEvaluator. Call the getExpressionEvaluator() method of a JspContext object.

Step 2: Parse the Expression. Invoke the parseExpression() method of the expression evaluator, passing the following parameters: the expression (as a String), its expected type (as a Class), and an optional function mapper. The parseExpression() method returns an Expression object, whose evaluate() method is called at the fourth step.

Step 3: Get a VariableResolver. Use the getVariableResolver() method of the JSP context to get the object that must be passed to evaluate() at the next step.
Resources

Use the following resources to test the examples and to learn more about the JSP 2.0 EL API.

Download the source code. The jspelapi_src.zip file contains the examples of this article: the jsputils directory groups the Java classes, and jspelapi is a Java Web application. In order to run the examples, you need J2SE, a J2EE 1.4 application server, JSTL 1.1, and a database server.

Read "Creating JSP 2.0 Tag Files." Andrei Cioroianu shows how to create and use tag files and how to transform existing page fragments into tag files. He uses JSTL and several advanced JSP features to build tag files that update and query a database.

Download OC4J 10g. OC4J 10g fully implement the J2EE 1.4 specs, which include JSP 2.0. You may use OC4J 10g (10.0.3) to test the examples. It works with all major database servers, including—of course—Oracle Database. Don't forget to configure the dbtags datasource and make sure that the proper database driver is available.

Download JSTL 1.1. Before deploying the jspelapi Web application, download JSTL and copy the jstl.jar and standard.jar files into the jspelapi/WEB-INF/lib directory.

Read the JSP 2.0 specification. The JSP 2.0 specification has an entire chapter dedicated to the EL API ("Part II: Chapter JSP.14 Expression Language API"). The expression language is described in another chapter ("Part I: Chapter JSP.2 Expression Language").

JSP samples and tutorials.
JSP Sample Code
Tutorial: Understanding the New Features of JSP 2.0

Step 4: Evaluate the Expression. Call the evaluate() method of the Expression object. The third and fourth steps can be repeated every time you want to evaluate the parsed expression. Note that at different moments, the same expression may have different values, depending on the values of its variables.

Wrapping parsed expressions. Our PEWrapper class has two fields that maintain references to a parsed Expression and its JspContext. This allows us to keep the Expression object together with the JspContext instance that is needed later for obtaining the variable resolver. The getInstance() method performs the first and second steps described above, returning a PEWrapper object. The nonstatic evaluate() method executes the third and fourth steps, returning the value of the expression. We also need a static evaluate() method that can be mapped to an EL function that is named evaluate3() in our el.tld file. The getInstance() method is mapped to an EL function too, named getPEWrapper() in el.tld.

Using parsed expressions. "Creating JSP 2.0 Tag Files" presents a JSP example (select.jsp) that queries a database using a tag file (select.tag). The tag file builds a SQL statement and uses the <sql:query> tag of JSTL to execute it. Then, the tag file iterates over the rows of the result set with <c:forEach>, executing the tag body with <jsp:doBody>. Therefore, the custom tag that invokes the tag file (<db:select>) performs a loop, and the JSP code between <db:select> and </db:select> is executed at each iteration. In our example, this code outputs the rows of an HTML table, parsing and evaluating the EL expressions again and again:

<db:select var="row" table="People" orderBy="name">
    <tr>
        <td> ${row.userID} </td>
        <td> ${row.name} </td>
        <td> ${row.email} </td>
    </tr>
</db:select>

We could view the body of <db:select> as a single JSP expression, which can be stored in a JSP variable (rowExpr) with <c:set>. In order to avoid the evaluation of the expression by the JSP container, each $ character is escaped with a backslash. Therefore, rowExpr keeps the text of the expression and not its value:

<c:set var="rowExpr">
    <tr>
        <td> \${row.userID} </td>
        <td> \${row.name} </td>
        <td> \${row.email} </td>
    </tr>
</c:set>

The expression is parsed using our PEWrapper class, whose instance is returned by el:getPEWrapper(). A reference to the PEWrapper object is kept in a JSP variable named parsedRowExpr:

<c:set var="parsedRowExpr" 
    value="${el:getPEWrapper(rowExpr,
        'java.lang.String', pageContext, null)}"/>

Now, we have a parsed expression that can be evaluated much faster with our el:evaluate3() function that invokes the static evaluate() method of PEWrapper:

<db:select var="row" table="People" orderBy="name">
    ${el:evaluate3(parsedRowExpr)}
</db:select>

Optimizations usually mean more programming, and in many cases, the code becomes less readable. However, using parsed expressions, the JSP container doesn't have to repeat the same operations again and again. Most of the JSP expressions don't need optimizations, but if a loop has many iterations and uses complex EL expressions, you should consider optimizing the code. Of course, the JSP container itself might cache the parsed expressions, but you can't be sure that this happens unless it's documented. The source code of the PEWrapper class is followed by the select.tag file and by the optimized version of the select.jsp page.

Conclusion

The JSP 2.0 expression language can improve the maintainability of your Web pages considerably. With the EL API, you can integrate the same language with other technologies, such as those based on XML. The EL has only one disadvantage: the JSP expressions are slower than the compiled Java code, but in most cases the EL overhead is not significant. When it is, you can use the EL API to optimize the expression evaluation process.


Andrei Cioroianu (devtools@devsphere.com) is the founder of Devsphere, a provider of Java frameworks, XML consulting, and Web development services. Andrei has written many Java articles published by ONJava, JavaWorld , and Java Developer's Journal He also coauthored the books Java XML Programmer's Reference and Professional Java XML (both from Wrox Press).



Please rate this document:

Excellent Good Average Below Average Poor


Send us your comments

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy