JSP 2.0 EXPRESSION LANGUAGE
The December 22, 2003 Tech Tip titled The JavaServer Pages Standard Tag Library (JSTL) showed how to use some of the new features of JSP 2.0, including the new Expression Language. The following tip covers the expression language in more detail. The sample code accompanying the tip is a Web application that contains a single JSP page. The page demonstrates several types of expressions and some odd conditions.
Why Another Language?
Before JSP 2.0, you could use only a scriptlet, JSP expression, or a custom tag to include server state in the JSP page output. Although these solutions are useful, they require a relatively large amount of work for even the simple job of accessing server-side state. JSP 2.0 incorporates the Expression Language (EL) first introduced in JSTL 1.0. EL makes it easier to integrate server-side state with presentation output. Let's start with a quick review of scriptlets, JSP expressions, and custom tags.
Consider a Web application that maintains information about usage statistics in an object of class ServerStats
. Class ServerStats
has a method getUserCount
, which returns the number of users currently logged on. The application maintains a single instance of ServerStats
in a ServletContext
attribute called stats. Here's a comparison of how you could create usage statistics output using a scriptlet, a JSP expression, and a custom tag:
- Creating output with a scriptlet: With JSP version 1, you can print the number of users logged on to the system using a scriptlet. Any output the scriptlet produces has to be explicitly written to the
out
stream. A JSP page could report the number of users with the following scriptlet and template text: There are currently <%
ServerStats ss =
(ServerStats)application.getAttribute("stats");
out.print(ss.getUserCount());
%> userslogged on.
The result would look something like this:
There are currently 15 users logged on.
- Creating output with a JSP expression: You can simplify things a bit by placing the code in a JSP expression. The expression text within the JSP expression is evaluated, and the result is coerced to a String for output. The result of the expression replaces the expression tag in the JSP page output. Writing the resulting text to the JSP page output is implicit.
There are currently
<%= (ServerStats)(application.getAttribute("stats")).
getUserCount() %> users logged on.
This is somewhat easier to write, but is still a bit difficult to read and maintain.
- Creating output with a custom tag: In JSP 1, the way to make dynamic content on a page more readable and reusable is to create custom tags. Custom tags are JSP page tags defined by a programmer. The behavior of the tag is implemented in a tag handler class, which must be written (usually in Java). A tag library descriptor (TLD) file defines the correspondence between the custom tag and its handler class. A Web container uses the TLD file and the custom tag class to generate code dynamically. Custom tags produce a cleaner JSP page and enable code reuse. Remembering that the actual code that implements the lookup is in the handler class, the JSP code for the example message would look like this:
There are currently <myTags:userCount/> users logged in.
Of course, to get to such a clean presentation, a developer would have to write both a Java class and an XML-format TLD file.
JSP 2.0 now offers a expression language that makes accessing server-side state even easier. The syntax of the expression language is simpler than a custom tag, and usually requires no associated Java code. Also, the expression language automatically handles typecasting, null values, and error handling. The JSP 2.0 expression language provides a way to access server-side state with less effort than the approaches shown above. The JSP 2.0 way to print the "number of users" message would look like this:
There are currently ${stats.userCount} users logged in.
Where EL Expressions Can Be Used
The JSP 2.0 specification describes the expression language in detail. The expression language is an extension of the JSTL 1.0 EL, that adds several new features.
A JSP 2.0 EL expression is always written between the delimiters ${
and }
. In the JSP page response, the result of expression evaluation replaces the expression and its delimiters in the template text. Within tags, expressions can be used only in attribute values. For example, the following code from the sample page is legal, because it uses tags only in template text and in attributes.
<ul>
<c:forEach var="k" items="${colors}">
<li><font color="${k}">This line is ${k}</font>.
</c:forEach>
</ul>
Here's an example of a tag that is used illegally:
<${tag} var="x"/> <%-- INVALID SYNTAX --%>
The variables in an EL expression are Web-tier state. They are values that can be in any scope: page, request, session, or application. Unless you specify otherwise, variables are in page scope.
In general, the values of EL variables are objects. When a variable's value is an enterprise beans reference, data from the enterprise bean can be included in the page with little difficulty. For example, a shopping application could keep a reference to a ShoppingCart
stateful session bean in an HttpSession
attribute, under the name "cart". Printing the number of items in the cart would then be as simple as the following:
You have ${cart.itemCount} items in your cart.
EL Syntax
EL expressions are comprised of literals, operators, and variables. Expressions can also contain functions, but these are not covered here. See the JSP 2.0 specifications and the J2EE 1.4 Tutorial for details.
Literals can be of type boolean ("true
" or "false
"), integer, floating point, or string. The token "null
" denotes the null literal.
The language offers the following operators (parentheses here are used only for grouping):
- binary logical operators
(<, <=, ==, >=, >, lt, le, eq, ge, gt)
- binary arithmetic operators
(+, -, *, /, div, %, mod)
- indexing operators
(.) and ([])
- (equivalent) unary negation operators
(!, not)
- arithmetic unary minus
(-)
- (empty) operator, which evaluates to true if the expression evaluates to null, or evaluates to a container object that contains no items.
- ternary operator for simple if-then-else
(?:)
- parentheses for controlling evaluation precedence
The operators with the highest precedence are "[]
" and ".
", in that order, followed by ()
. The precedence for the rest of the operators is conventional: see the JSP 2.0 specifications for details.
Literal Expressions
Any expression containing only literals and operators are evaluated as arithmetic expressions. This is shown in the sample page for the tip:
Expression |
Value |
${1} |
1 |
${1==1} |
true |
${1==2} |
false |
${22/7} |
3.1428... |
${123*234} |
27872 |
Some other important changes to the platform include:
Variable names in expressions evaluate to values of a type appropriate for the operation in which it is involved. For example, the expression ${x + 1}
tries to coerce x
to a number before performing the addition.
EL Variables
Variables are accessed by name. Type coercion is automatic from the variable's native type to a type that is compatible with the requested operation. Variables can be either defined by the page, or can be JSP implicit objects. (See the JSP 2.0 specifications and the J2EE 1.4 tutorial for a complete list of implicit objects.)
Variable names can be composed with expressions involving the operators ".
" and "[]
". These operators provide a great deal of the power of EL, because they work for all collection types, as well as for Java language arrays, maps, lists, and JavaBeans property accessors. The operators allow you to navigate through a network of JavaBeans or enterprise beans. You can move from one bean to the next by simply dereferencing or indexing one of its properties each time you use the operator.
The ".
" Operator
The ".
" operator is shorthand for calling a JavaBeans property accessor for the property whose name is on the right-hand side of the operator. For example, the following expression in the sample page:
${pageContext.servletContext.servletContextName}
actually executes as:
pageContext.getServletContext().getServletContextName()
EL is more forgiving than is the Java programming language regarding null values in expressions. In the Java programming language, the expression a.getB().getC()
throws a NullPointerException
if a.getB()
returns null. Not so in EL. The EL expression ${a.b.c}
returns null if ${a.b}
is null -- it does not throw an exception. This makes writing expressions easier. To check for null explicitly, use either the empty operator, or compare the expression to null using == or the is
keyword, like this:
<c:if test="${a.b is null}">
<c:if test="${empty a.b}">
<c:if test="${a.b == null}">
If the object being accessed is a Map
, the ".
" operator uses the name of the right-hand side as a string literal, and uses it as a key to fetch the result. This is demonstrated in the sample code in the two following expressions. The expressions are equivalent (both reference the host
HTTP header):
${header["host"]}
${header.host}
The "[]
" Operator
The []
operator is a polymorphic indexing operator that can be used to index collections (including Maps
and Lists
) and arrays. The value inside the brackets is used as a key into a map, or as a List
or array index. On the sample page, the []
operator is used to access a List
and a Map
:
${colors[1]} |
orange |
${colors[5]} |
violet |
${colors[1] > colors[5]} |
false |
${colors[1024]} |
|
${colors[1024] == null} |
true |
Note that ${colors[1024]}
has no value (because the array isn't that long). Evaluating the expression produces null, not an "out of bounds" error.
The []
operator can also be used to call JavaBeans property accessors. In that case, the string in the bracket is the property name. For example, given a JavaBean a
, both of the expressions ${a["b"]}
and ${a.b}
evaluate to the Java method call a.getB()
.
The ".
" and []
operators are almost, but not quite, equivalent for accessing Maps. The difference between the two is illustrated below:
${header["host"]} |
localhost:8080 |
${header.host} |
localhost:8080 |
${header["user-agent"]} |
Mozilla/5.0 (Macintosh;...) |
${header.user-agent} |
0 |
Notice that ${header["host"]}
and ${header.host}
both look up the key host
in the Map
called header (which is a JSP implicit object containing HTTP headers for the request). But, while ${header["user-agent"]}
produces the name of a browser, ${header.user-agent}
produces 0
. What does this mean?
The problem here is that user-agent
isn't a well-formed Java identifier. The value is 0
because the expression evaluator parses the expression as the difference of the variable header["user"]
minus the value of the (nonexistent) variable agent
. Both of these values are null, so the result is 0
(this is by convention defined in the specification). The lesson here is: it's best to always use []
for indexing Maps
, and use ".
" for calling property accessors.
A final difference between "[]
" and ".
" is that the value of an expression can be used to indicate a name using "[]
". The example in the sample code looks like this:
<c:set var="headerName" value="host"/>
${header[headerName]}
The result of this block is the same as for the other two expressions that look up the host
header: "localhost:8080
". The first line sets the page variable headerName
to the string host
. The second line uses the value of the variable to look up the HTTP header host
. Although this code simply uses a variable name, the contents of the "[]
" could be any EL expression that yields the name of an HTTP header.
Controlling Variable Scope
Unless defined to be otherwise, variables defined on a page with the assignment operator "=
", or with a JSTL <c:set>
tag are in page scope (in other words, they are PageContext
attributes). If a variable doesn't indicate its scope, the expression evaluator searches attributes in page, request, session, and application scope, and returns the first one it finds. If no variable of that name is found, the expression evaluates to null.
A variable in an explicit scope can be accessed using one of the four implicit scope objects: pageScope
, requestScope
, sessionScope
, or applicationScope
. For example, ${sessionScope.myVariable}
returns the value of the session attribute myVariable
. A variable's scope can be defined using the scope attribute of <c:set>
, for example:
<c:set var="myVariable" value="1" scope="session"/>
Defining Default Values
Like many scripting languages, EL is forgiving of null values. When it can, EL simply substitutes an empty string, or zero, for null. The problem with this behavior is that sometimes you want to have special behavior if the value is null.
If you are using JSTL, the <c:out>
tag has a default attribute you can use in case the value you're trying for is null. This example from the sample page shows how to use it:
<c:out value="${colors[1024]}" default="transparent"/>
The result is the string transparent
. To do the same thing using pure EL, you can use the ternary ?:
operator, like this:
${colors[1024] == null ? "transparent" : colors[1024]}
This expression also returns transparent
, for the same reason as before.
Iterating Maps
When using the JSTL tag <c:forEach>
to iterate on the values of a Map
, the iteration variable is of type java.util.Map.Entry
. To access the key and value of the map entry, use .key
and .entry
on the iteration variable name.
The sample page shows an example of this, dumping the HTTP headers to the page:
<c:forEach var="hdr" items="${header}">
${hdr.key}=${hdr.value}<br>
</c:forEach>
RUNNING THE SAMPLE CODE
Download the sample archive for these tips. The application's context root is ttjan2004. The downloaded ear file also contains the complete source code for the sample.
You can deploy the application archive (ttjan2004.ear) on the J2EE 1.4 Application Server using the deploytool program or the admin console. You can also deploy it by issuing the asadmin
command as follows:
asadmin deploy install_dir/ttjan2004.ear
Replace install_dir with the directory in which you installed the ear file.
You can access the application at http://localhost:8000/ttjan2004
.
For a J2EE 1.4-compliant implementation other than the J2EE 1.4 Application Server, use your J2EE product's deployment tools to deploy the application on your platform.
When you start the application, you should see the following page (only part of the page is shown):
from:
http://java.sun.com/developer/EJTechTips/2004/tt0126.html