Using the Expression Language in JSPs - Creating Enterprise Applications - PROFESSIONAL JAVA FOR WEB APPLICATIONS (2014)

PROFESSIONAL JAVA FOR WEB APPLICATIONS (2014)

Part I Creating Enterprise Applications

Chapter 6 Using the Expression Language in JSPs

IN THIS CHAPTER

· All about Expression Language

· How to write with the EL syntax

· How to use scoped variables in EL expressions

· How to access collections with Java 8 streams in EL expressions

· Switching out Java code with Expression Language

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the wrox.com code downloads for this chapter at www.wrox.com/go/projavaforwebapps on the Download Code tab. The code for this chapter is divided into the following major examples:

· User-Profile Project

· Customer-Support-v4 Project

NEW MAVEN DEPENDENCIES FOR THIS CHAPTER

In addition to Maven dependencies introduced in previous chapters, you also need the following Maven dependency:

<dependency>

<groupId>javax.el</groupId>

<artifactId>javax.el-api</artifactId>

<version>3.0.0</version>

<scope>provided</scope>

</dependency>

UNDERSTANDING EXPRESSION LANGUAGE

Up to this point, you have used Java to output dynamic content from your JSPs. However, recall that Chapter 4 covers how the use of declarations, scriptlets, and expressions is discouraged. Not only does this provide a great deal of power (sometimes too much) to your JSPs, but it also makes writing JSPs difficult for UI developers with little or no Java background. There must be an easier way to display data and perform simple operations than using Java code. You might think that the <jsp> tags could provide a solution, and indeed these tags can be used to replace certain Java operations. However, these tags are clunky and awkward to use. What’s needed is something easily read, familiar to both Java developers and UI developers, and with a simple set of rules and operators to make data access and manipulation easier.

What It’s For

Expression Language (EL) was originally developed as part of the JSP Java Standard Tag Library (JSTL), something you learn about in the next chapter, to support the rendering of data on JSP pages without the use of scriptlets, declarations, or expressions. It was inspired by and largely based on the ECMAScript (the foundation of JavaScript) and XPath languages. At the time it was referred to as the Simplest Possible Expression Language (SPEL) but later shortened to Expression Language. EL was part of the JSTL 1.0 specification that came out with JSP 1.2 and could be used only in attributes of JSTL tags. In JSP 2.0 and JSTL 1.1 the EL specification (due to its popularity) was moved from the JSTL specification to the JSP specification and became available for use anywhere in a JSP, not just within JSTL tag attributes.

While this was happening, work had commenced on JavaServer Faces, built on JSP 1.2 as an alternative to plain JSP. JSF also needed its own expression language. However, there were several drawbacks to reusing the EL as it existed for JSPs. For one, JSF needed to control the evaluation of expressions to certain points of the JSF life cycle. An expression might need to be evaluated during page rendering but also during a postback to the JSF page. In addition, JSF needed better support for method expressions than the EL offered. As a result, two separate but extremely similar expression languages formed — one for JSP 2.0 and one for JSF 1.0.

Obviously having two separate Java expression languages was not ideal, so when work began on the JSP 2.1 specification, an effort was underway to merge the JSP 2.0 Expression Language with the JSF 1.1 Expression Language. The result was the Java Unified Expression Language (JUEL) for JSP 2.1 and JSF 1.2.

Despite being shared by JSP and JSF, EL did not get its own JSR but continued to be a part of the JSP specification, although it did have its own specification document and JAR artifact. This remained the case for EL in JSP 2.2. EL continues to expand and improve, and as of Java EE 7 it was moved into its own JSR (JSR 341) and updated to support lambda expressions and an equivalent of the Java 8 Collections Stream API, marking Java Unified Expression Language 3.0 (or EL 3.0 for short). EL 3.0 was released with Java EE 7, Servlet 3.1, JSP 2.3, and JSF 2.2 in 2013. In this chapter you explore EL 3.0 as it pertains to JSPs, learning about JSF-related features only where pertinent comparisons can be made. Most of the chapter centers on syntax, and where features that are new to EL 3.0 are demonstrated, this is indicated.

Understanding the Base Syntax

The base syntax for EL delineates expressions that require evaluation from the rest of the JSP page syntax. The JSP interpreter must detect when an EL expression begins and ends so that it can parse and evaluate the expression separately from the rest of the page. There are two different types of the base EL syntax: immediate evaluation and deferred evaluation.

Immediate Evaluation

Immediate evaluation EL expressions are those that the JSP engine should parse and evaluate at the time of page rendering. This means that as the JSP code is being executed from top to bottom, the EL expression is evaluated as soon as the JSP engine comes across it and before the execution of the rest of the page continues. EL expressions that should be immediately evaluated look like the following example, where expr is a valid EL expression.

${expr}

The dollar sign and opening and closing brackets define the boundaries of the EL expression. Everything inside the brackets gets evaluated as an EL expression. More important, this means that you can’t use this syntax for any other purpose in your JSPs; otherwise, it will get evaluated as an EL expression and could result in an EL syntax error. If you ever needed to write something with this syntax out to the response, you would need to escape the dollar sign:

${not an EL expression}

The backslash before the dollar sign indicates to the JSP engine that this is not, in fact, an EL expression and should not be evaluated. The previous example would have literally been written to the response as ${not an EL expression}. You could also have used the dollar sign XML entity $ instead of $ and it would have resulted in the same outcome.

${not an EL expression}

Although the JSP engine would also ignore this, many find using the backslash easier. It’s simply a matter of personal preference. Of course, you might legitimately need to put a backslash before an expression that you do, actually, want evaluated. This requires the use of the backslash XML entity:

\${EL expression to evaluate}

In this case, the EL expression will be evaluated and rendered after the backslash.

Deferred Evaluation

Deferred evaluation EL expressions are a part of the Unified Expression Language that primarily supports the needs of JavaServer Faces. Although the deferred syntax is legal in JSPs, it is not normally seen in JSPs. Deferred syntax looks nearly identical to immediate syntax, again where expr is a valid EL expression:

#{expr}

In JSF, deferred expressions can be evaluated either when the page is rendered or during a postback to the page, or possibly even both. The specifics of this are not pertinent to this book, but you must understand that this is different from JSP, which does not have a sense of life cycles that JSF has. In JSP, the #{} deferred syntax, which is only valid in JSP tag attributes, can be used to defer the evaluation of the EL expression until later in the rendering process of the tag. Instead of the EL expression being evaluated before the attribute value is bound to the tag (like it would be with ${}), the tag attribute gets a reference to the unevaluated EL expression. The tag can then later invoke a method to evaluate the EL expression when it’s appropriate. This can be useful and is explored more in Chapter 8, but it is rarely used.

One potential problem with deferred syntax is that some templating languages and JavaScript frameworks use the #{} syntax for substitutions. Because of this, if you use these substitutions, you would normally have to escape them so that they aren’t confused with deferred evaluation EL expressions:

\#{not an EL expression}

#{also not an EL expression}

However, this may not work for some frameworks that utilize this syntax, and it can be a real pain if you need to use this often or if you have a lot of existing JSPs that need to work with EL 2.1 or higher. (Also, the XML entity isn’t compatible with JavaScript.) Because of this, there is another option for preventing a #{} literal from being evaluated as a deferred expression. Within the <jsp-config> section of the deployment descriptor, you can add the following tag to any <jsp-property-group>:

<deferred-syntax-allowed-as-literal>true</deferred-syntax-allowed-as-literal>

This permits the #{} syntax to be used in a literal manner and prevents you from having to escape the hash tag in this case. If you need to control this for individual JSPs, you can use the deferredSyntaxAllowedAsLiteral="true" attribute of the page directive in any JSP, instead.

For the remainder of this book, you will only see immediate evaluation EL syntax in example code and you will not use deferred evaluation EL syntax, with one exception. In the Chapter 8 discussion on custom tag and function libraries, you’ll explore the <deferred-value> and <deferred-method> options when defining custom tags. This also necessitates demonstrating the deferred syntax.

Placing EL Expressions

Simply put, EL expressions can be used just about anywhere in a JSP, with a few minor exceptions. To start, EL expressions cannot be used within any directives, so don’t even try it. Directives (<%@ page %>, <%@ include %>, and <%@ taglib %>) are evaluated when the JSP is compiled, but EL expressions are evaluated later when the JSP is rendered, so it cannot work. Also, EL expressions are not valid within JSP declarations (<%! %>), scriptlets (<% %>), or expressions (<%= %>). If used within any of these, an EL expression will simply be ignored or, worse, could result in a syntax error.

Other than that, EL expressions can be placed just about anywhere. One place you might see EL expressions is within simple literal text written to the screen:

The user will see ${expr} text and will know that ${expr} is good.

This example includes two EL expressions that, when evaluated, are placed inline with the text that displays. If the first expression evaluated to “red” and the second expression evaluated to “it,” the user would see the following:

The user will see red text and will know that it is good.

In addition, expressions can be used within standard HTML tag attributes as in the following example.

<input type="text" name="something" value="${expr}" />

HTML tag attributes are not the only place that EL expressions are allowed. You can also use them in JSP tag attributes, as demonstrated with the following code.

<c:url value="/something/${expr}/${expr}" />

<c:redirect url="${expr}" />

As you can see, EL expressions do not have to make up the entire attribute value. Instead, any one or more parts of the attribute value can include EL expressions. You might wonder about other HTML features, such as JavaScript or Cascading Style Sheets. The JSP engine does not parse things of this nature and writes them out to the response as if they were literal text, so these, also, may contain EL expressions in either quoted or literal form:

<script type="text/javascript" lang="javascript">

var employeeName = '${expr}';

var booleanValue = ${expr};

var numericValue = ${expr};

</script>

<style type="text/css">

span.error {

color: ${expr};

background-image: url('/some/place/${expr}.png');

}

</style>

So far you have learned about the different types of EL expressions and where EL expressions can be placed, but you may wonder what exactly expr looks like. In the next section you learn about what you can put within an EL expression.

WRITING WITH THE EL SYNTAX

EL expressions, like any other language, have a specific syntax. Like Java, JavaScript, and most other languages, that syntax is strict, and violating it will result in syntax errors when your JSP is rendered. Unlike Java, however, EL syntax is loosely typed and has many implicit type conversions built in, similar to languages like PHP or JavaScript. The primary rule for an expression is that it should evaluate to some value. You cannot declare variables within an expression or perform some kind of assignment or operation that does not result in a value. (For example, ${object.method()} is only valid if method has a non-void return type.) EL is not designed to replace Java; instead, it is designed to provide you with the tools you need to create JSPs without Java.

NOTE Although you cannot declare variables within an EL expression, you can assign variables as of the EL 3.0 specification. Using the standard assignment operator =, you can assign A = B within an expression as long as B is some value that can be written out to the page. So, the expression ${x = 5} will result in assigning 5 to x and also in rendering 5 in place of the EL expression.

Reserved Keywords

As with any other language, one of the first things you should know about EL is its list of reserved keywords. These are words that should be used only for their prescribed purpose. Variables, properties, and methods should have names equal to these reserved words.

· true

· false

· null

· instanceof

· empty

· div

· mod

· and

· or

· not

· eq

· ne

· lt

· gt

· le

· ge

You’ll recognize the first four words as also being Java reserved keywords. You can use these in the same manner you would use their counterparts in Java. The empty keyword is used to validate whether some Collection, Map, or array contains any values, or whether some String has a length of one or more characters. If any of these are null or “empty,” the expression evaluates to true; otherwise, it evaluates to false.

${empty x}

The div and mod keywords map to the Java mathematical operations divide (/) and modulus (%), respectively, and are merely alternatives to the mathematical symbols. You can still use / and % if you prefer. The and, or, and not keywords map to the Java logical operators &&, ||, and !, respectively. As with the mathematical operators, you can still use traditional logical operators if you prefer. Finally, the eq, ne, lt, gt, le, and ge operators are alternatives to the Java relational operators ==, !=, <, >, <=, and >=, respectively, which can also still be used if you prefer.

Operator Precedence

Just like with other languages, all the previous operators, together with other operators in the EL, have an order of precedence that is important to understand. This order is mostly intuitive and not dissimilar from operator precedence in Java. More important, as with Java and arithmetic equations, operators of equal precedence are considered in the order they appear in an expression, from left to right.

The first operators evaluated in an EL expression are the bracket [] and dot (.) resolution operators. Consider the following expression:

${myCollection["key"].memberName["anotherKey"]}

The engine first resolves the value mapped to key in the myCollection object. It then resolves the memberName method, field, or property within the key value found in myCollection. Finally, it locates the anotherKey value within the value that memberName evaluates to. After these operators are considered, the grouping parentheses operators () are considered. These operators are used to change the precedence of other operators, just as they are in Java or arithmetic equations.

The third set of operators considered includes the unary negative sign (-), not, !, and empty. Next, the EL engine evaluates the arithmetic operators multiply (*), divide (/), and div, and modulus (%) and mod, which are followed by the addition (+) and binary subtraction (−) operators, just like they are ordered in mathematical equations. After this the EL string concatenation operator += (new to EL 3.0) is evaluated. Next, it evaluates the comparison relational operators < (or lt), > (or gt), <= (or le), and >= (or ge), followed by the equality relational operators == (or eq) and != (or ne). After this, it evaluates all the && and and operators from left to right, then all the || and or operators from left to right, and then all the ? and : conditional operators from left to right.

The next thing that the EL engine evaluates is the lambda expression operator (->), new in the EL 3.0 specification. This has the same syntactic and semantic purpose as the Java 8 lambda expression operator. However, you do not need to be running on Java 8 for EL lambda expressions to be valid. After this, the EL engine evaluates the assignment = operator, which was also added in the EL 3.0 specification. This operator assigns the value of some expression on the right side of the operator to the variable on the left side of the operator. The resulting value of the expression is then the value of the variable on the left side of the operator. Consider the following expression.

${x = y + 3}

Now assume that, at execution time, the value of y is 4. The result of the expression y + 3 is 7, thus 7 is assigned to x. Because the resulting value of the expression is x, the value of ${x = y + 3} is 7.

The final operator that the EL engine evaluates is the semicolon (;) operator, also a new feature in EL 3.0. This operator mimics the comma (,) operator in C, allowing the specification of several expressions with the values of all but the last expression being discarded. To understand this, refer to the following expression.

${x = y + 3; object.callMethod(x); 'Hello, World!'}

This combination EL expression has four expressions within it.

· The expression y + 3 is evaluated, resulting in 7 assuming y is 4.

· That value is assigned to x.

· The callMethod method is invoked on the object variable and passed x (7) as its argument.

· The string literal “Hello, World!” is evaluated. The result of this expression is the result of the expression after the last semicolon only: “Hello, World!”

The results of the x = y + 3 expression and the object.callMethod(x) expression are discarded. This is especially useful to assign some value to an EL variable and then include that value in some other part of the expression instead of just outputting the value.

To help you keep all this straight, the following list summarizes the order of precedence from top (highest) to bottom (lowest) with only the symbols and none of the clutter. Remember that operators with the same precedence are evaluated in the order they appear in an expression, from left to right.

1. [], .

2. ()

3. unary -, !, not, empty

4. *, /, div, %, mod

5. + math, binary -

6. += string

7. <, lt, >, gt, <=, le, >=, ge

8. ==, eq, !=, ne

9. &&, and

10.||, or

11.?, :

12.->

13.=

14.;

NOTE In Java, you test for equality between objects using the equals method. For example, to test if two Strings were equal, you would use "Hello".equals("Hello"), not "Hello" == "Hello". The latter tests that the two references are the same instance, not that the two objects are equal. However, in EL expressions you use the == or eq operators to test for object equality instead of calling the equals method. (There is no equivalent for testing if two references are the same in EL.) Likewise, you use !=or ne instead of !"Hello".equals("Hello").

The use of the relational comparison operators <, lt, >, gt, <=, le, >=, and ge is similar to the equality operators. Any two objects that implement the java.lang.Comparable interface can be compared with comparison operators as long as the types are the same or one can be coerced to the other. So, ${o1 >= o2} and ${o1 ge o2} in EL are equivalent to o1.compareTo(o2) >= 0 in Java, and ${o1 < o2} and ${o1 lt o2} are equivalent to o1.compareTo(o2) < 0.

Literal Values

The Unified Expression Language has a support for specifying literal values with a specific syntax. You have already seen the true, false, and null keywords, which are all literal values.

In addition, EL can have string literal values. Unlike Java, where string literals are always surrounded by double quotes, string literals in EL can be surrounded by either double or single quotes, similar to PHP and JavaScript. So, both of the expressions in the following example are valid.

${"This string will be rendered on the user's screen."}

${'This string will also be "rendered" on the screen.'}

As you can see, there are advantages and disadvantages to using either type of string literal, and in many cases you will simply use the one that’s easiest for the particular case. If some string has a single quote within it, it’s probably easiest to use double quotes for the literal. Similarly, if the string has a double quote in it, it’s probably easiest to use a single quote literal.

One thing you must be careful about, however, is using EL expression string literals within JSP tag attributes. Because these are both evaluated by the JSP engine, the quotes surrounding an attribute value and the quotes surrounding a string literal conflict. Thus, both of the EL expression attribute values in the following example are invalid and result in syntax errors:

<c:url value="${"value"}" />

<c:url value='${'value'}' />

There are two valid ways to address this conflict. You can either use opposite quote types for the attribute and literal, or you can escape the literal quotes. All four lines of code in the following example are valid.

<c:url value="${'value'}" />

<c:url value='${"value"}' />

<c:url value="${\"value\"}" />

<c:url value='${\'value\'}' />

Generally, you will find it is easier to simply use opposite quotes instead of escaping. But what if your string literal itself contains a single or double quote and you need to put the expression in an attribute value? There’s no way around it at this point. You must escape something. The six lines of code in the following example are all valid ways of dealing with this.

<c:url value="${'some \"value\"'}" />

<c:url value='${"some \"value\""}' />

<c:url value="${'some \'value\''}" />

<c:url value='${"some \'value\'"}' />

<c:url value="${\"some 'value'\"}" />

<c:url value='${\'some "value"\'}' />

Need to mix and match single and double quotes within a string literal that exists within an attribute value? This is where things start to get hairy:

<c:url value="${'some attribute\'s \"value\"'}" />

<c:url value='${"some \"attribute\" \'value\'"}' />

As you can tell, this can quickly spiral out of control. Where possible, it’s best to keep your string literals simple. The last thing to note about string literals is that, as of Expression Language 3.0 in Java EE 7, you can concatenate string literals within EL expressions somewhat like you do within Java. All three lines in the following example are equivalent and result in the same output.

The user will see ${expr} text and will ${expr}.

${'The user will see ' += expr += " text and will " += expr += '.'}

${"The user will see " += expr += ' text and will ' += expr += "."}

If expr results in some object that is not a String, it will be coerced to a String by calling the toString method on that object.

Numeric literals in EL are simplified over those in Java, and you can even perform arithmetic between certain objects that you could not in Java. Consider the following three integer-type numeric literals:

${105}

${-132147483648}

${139223372036854775807}

· The first literal is an implicit int and is treated like one when the expression is evaluated.

· The second literal is too large to be an int. In Java, this would be a syntax error unless you appended an L to the end of the number to indicate it was a long, but in EL it simply becomes a long implicitly.

· The third literal is too large to even be a long, so it is treated as a BigInteger implicitly.

All these conversions happen under the hood without your involvement. Then there are the decimal types:

${105.509}

${3400000000000000000000000000000000000001.0}

${1.79769313486231570e+309}

Similar to the integer types, these literals are an implicit float, double, and BigDecimal, respectively. It should be noted that although the default literal decimal type in Java is double, the default decimal type in EL is float unless a larger precision is required. Keep this in mind when you work with EL expressions. You cannot explicitly specify the literal type — it is always handled implicitly.

EL expressions make mathematical operations much easier because all type conversions and precision upgrades are implicit and because the arithmetic operators can be used on BigInteger and BigDecimal types. Consider the following expression, which adds two numbers and returns the resulting value:

${12 + 1.79769313486231570e+309}

The number on the left side of the addition operator is an int, whereas the number on the right side is an implicit BigDecimal. To do this in Java would normally require the following code:

new BigDecimal(12).add(new BigDecimal("1.79769313486231570e+309"));

However, the EL engine takes care of everything for you. First, it coerces 12 from an int to a BigDecimal; then it turns the addition operator into a call to the add method.

NOTE In Java, numbers can be expressed as standard (base-10, 83) literals, octal (base-8, 0123) literals, hexadecimal (base-16, 0x53) literals, or binary (base-2, 0b01010011) literals. In EL expressions, only base-10 literals are permitted. There is no equivalent for literals in the other bases. Also, while underscores are permitted within numeric literals (1_491_188, 0b0101_0011) in Java to make it easier to distinguish groups of numbers in a literal (as a replacement for commas, for example), this is not permitted in EL expressions. Number literals must be contiguous.

Three other primitive literals to consider are chars, bytes, and shorts. You do not normally need to use these data types in EL expressions, but it is possible that some method you might call in an EL expression could expect a char, byte, or short as an argument. EL does not contain specific literals for these types but will coerce other literals into chars, bytes, and shorts when necessary.

For chars, a null, '' string literal, or "" string literal will be coerced into the null byte character (0x00). A single-character string literal (single or double quote) will be coerced into its equivalent char. An integer-type number will also be coerced into a char as long as its value is between 0 and 65,535. Any other type, any multicharacter string, or any number outside the range of 0 and 65,535 will result in an error.

Any integer-type number will also be coerced into a byte or short when necessary, as long as the number does not extend beyond the range of the byte or short it is being coerced to. Otherwise, the attempted coercion will result in an error.

The final literal type is not a primitive but rather a literal for creating various collections. Collection literals construction is a feature proposed as an improvement to the Java Collections API in Java 8 that did not make the final feature cut and instead was deferred to Java 9 (for now). It did make it into Expression Language 3.0, however. You can create a collection within an EL expression whenever needed. The syntax is rather intuitive, is quite similar to syntaxes in JavaScript and other languages, and is in line with the proposed syntax for Java 9. You can construct Sets, Lists, and Maps with EL collection literals, and they will all be constructed as instances of the default implementations. A literal Set will become a HashSet<Object>, a literal List will become an ArrayList<Object>, and a literal Map will become a HashMap<Object, Object>. Consider first the Set literal:

{1, 2, 'three', 4.00, x}

This constructs a HashSet<Object> with five elements of varying types. The fifth object, x, could be anything. Commas separate elements in the literal Set. You might need to create a Set, for example, to pass in as an argument to a method call:

${someObject.someMethod({1, 2, 'three', 4.00, x})}

Constructing a List is nearly identical to constructing a Set except that it uses brackets instead of braces, and it works exactly the same as arrays in JavaScript/JSON:

[1, 2, 'three', [x, y], {'foo', 'bar'}]

Notice that the fourth element of this ArrayList<Object> is another List, and the fifth element is a Set. You can nest collection literals in this manner to insert collection objects into other collection objects. As with Sets, elements in Lists are separated with commas.

The final collection literal, which creates a HashMap<Object, Object>, is identical to the object literal syntax in JavaScript and JSON:

{'one': 1, 2: 'two', 'key': x, 'list': [1, 2, 3]}

Elements here, too, are separated with commas. However, Maps are more complicated because they require keys mapped to values instead of just values. So each element in this literal is a pair of objects separated by a colon, with the object on the left of the colon being the key and the object on the right of the colon being the value. The list key in this literal is mapped to a List object with values 1, 2, and 3.

Object Properties and Methods

EL provides a simplified syntax for accessing properties in JavaBeans in addition to the standard syntax you are used to for accessing public accessor methods. You cannot access public fields from EL expressions. Consider a class named Shirt with a public field named size. Assuming a variable name of shirt, you might think that you could access size with the following EL expression:

${shirt.size}

However, this is not allowed. When the EL engine sees this syntax, it is looking for a property on shirt, not a field. But what is a property? Consider an altered Shirt where size is a properly encapsulated private field with standard JavaBean accessor and mutator methods getSize and setSize. Now the expression shirt.size becomes a shortcut for calling shirt.getSize(). This can work for any field of any type. As long as it has a standard JavaBean accessor method, it can be accessed in this way. If Shirt had a field namedstyleCategory with an accessor getStyleCategory, it could be accessed with shirt.styleCategory. For boolean fields (and only boolean fields) the accessor can start with either get or is. So for a field named expired with either a getExpired or isExpired accessor, you could access the field with shirt.expired.

This is not the only technique that you can use to access properties within a JavaBean. In the spirit of the ECMAScript and XPath languages, you can also access properties using the [] operator. The following expressions also access the size, styleCategory, andexpired properties using the getSize, getStyleCategory, and getExpired or isExpired methods, respectively.

${shirt["size"]}

${shirt["styleCategory"]}

${shirt["expired"]}

In earlier versions of EL, you could access only JavaBeans properties. You could not call methods on objects. However, EL 2.1 added the ability to call object methods in JSPs. So, you could get the size of a Shirt with ${shirt.getSize()} instead of ${shirt.size}, but why would you? The latter is certainly easier. Method invocation mostly comes in handy when a value-returning method also requires some input.

Suppose you had an immutable class ComplexNumber that represents mathematical complex numbers (combination of a real number and an imaginary number in the form a + bi). That class would undoubtedly have a plus method that enables you to add some other number to it. (Possibly that method is overloaded so that you could add an integer, a double, or another ComplexNumber.) You can call the plus method and pass in an argument, and the resulting ComplexNumber would be the value of the expression:

${complex.plus(12)}

In this example, the toString method is implicitly called on the resulting ComplexNumber so that the string representation of the ComplexNumber is rendered. However, suppose you wanted the i in the string representation to be properly italicized so that it looks like a proper mathematical representation of a complex number. You might have a toHtmlString method on the ComplexNumber class to achieve this. You can thus render it like so:

${complex.plus(12).toHtmlString()}

These are chained method calls identical to the way you would perform this operation in standard Java code.

EL Functions

In EL, a function is a special tool mapped to a static method on a class. Like schema-compliant XML tags, functions are mapped to a namespace. The overall syntax of a function call is as follows, where [ns] is the namespace, [fn] is the function name, and [a1]through [an] are arguments:

${[ns]:[fn]([a1[, a2[, a3[, ...]]]])}

Functions are defined within Tag Library Descriptors (TLDs), which may sound strange because functions are not tags. This is a carryover from the earliest days of EL when it was part of the Java Standard Tag Library (JSTL) specification and EL could be used only within JSP tag attributes. Because the TLD concept already supported the idea of namespaces, it made sense for EL function definitions to remain within TLDs.

You learn more about TLDs and defining tags and functions in Chapter 8. However, there is already a set of functions defined in the JSTL that meet many of the needs developers have within JSPs today. All the functions deal with strings in some way — trimming, searching, joining, splitting, escaping, and more. By convention, the JSTL function library has a namespace of fn; however, you may make it whatever you like in the taglib directive. You experiment with using EL functions in the next section, but here are some of the more common JSTL EL functions and what they do.

· ${fn:contains(String, String)} — This function tests whether the first string contains one or more instances of the second string and returns true if it does.

· ${fn:escapeXml(String)} — If a string you are outputting could contain special characters, you can use this function to escape those special characters. < becomes <, > becomes >, & becomes &, and " becomes ". This is an especially important tool in the prevention of cross-site scripting (XSS) attacks.

· ${fn:join(String[], String)} — This function joins an array of strings together using the specified string as a delimiter. For example, this could be useful for comma-separating an array of e-mail addresses together into one string for display on the page.

· ${fn:length(Object)} — If the argument is a string, this function invokes and returns the result of calling the length method on the specified string. If it is a Collection, Map, or array, it returns the size of that Collection, Map, or array. No other types are supported. This is perhaps the most useful function in the JSTL.

· ${fn:toLowerCase(String)} and ${fn:toUpperCase(String)} — You can use these functions to change the case of a string to all lowercase or all uppercase.

· ${fn:trim(String)} — This function trims all white space from both ends of the specified string.

There are still more functions available in the JSTL, and you can read about the rest of them by clicking. This is the documentation for JSTL 1.1 in Java EE 5. Unfortunately, there is no readily available HTML documentation for JSTL 1.2 in Java EE 6 and 7.

Static Field and Method Access

New in Expression Language 3.0, you can now access the public static fields and public static methods within any class on your JSP’s class path. You could argue (and some have) that this puts too much power in the hands of JSP authors and enables them to do practically anything they could normally do with a scriptlet. It’s up to you to decide whether that is a good thing or a bad thing, but the feature exists and cannot be disabled in EL 3.0.

You access static fields and methods the same way you would in Java — using the fully-qualified class name and field or method name separated with the dot operator. For example, you can access the MAX_VALUE constant on the Integer class with the following expression:

${java.lang.Integer.MAX_VALUE}

The class name must be fully qualified unless the class is imported using the JSP page directive. Remember that in JSPs, like Java, all classes in java.lang are implicitly imported for you. Because of this, the previous expression could be written like this instead:

${Integer.MAX_VALUE}

With this you can access static fields or methods on any class your JSP has access to. It’s important to note that you can only read the value of these fields. You cannot write to them. (Of course, if a field is also final, you couldn’t normally write to it anyway.) Calling a static method on a class is just as easy. Suppose you wanted to reverse the order of the bits in a number and see how the value of the number changed:

${java.lang.Integer.reverse(42)}

${Integer.reverse(24)}

This expression calls the static reverse method on the Integer class and passes the number 42 as its argument. In addition to calling named static methods, you can also invoke a constructor on a class, which returns an instance of that class that you can further access properties of, invoke methods on, or simply coerce to a string for output.

${com.wrox.User()}

${com.wrox.User('First', 'Last').firstName}

Although the static method access can entirely replace the behavior of EL functions and function libraries, that doesn’t mean that function libraries are unnecessary. The previous static method call to Integer.reverse might be convenient, but with a theoretical intfunction library mapped to the static methods of Integer, the following expression is still more convenient:

${int:reverse(42)}

That may not seem much shorter, but imagine a much longer class name, and you should quickly see why function libraries are still of great use. One of the areas in which static field access could be most handy is with enums, which you learn about next.

Enums

Chances are you’ve been exposed to Java enums at some point, and if you’ve been using Java for a while, you are probably familiar with how useful and powerful they can be. Traditionally, enums in EL have been coerced to and from strings when necessary. For example, say your JSP had an in-scope variable named dayOfWeek and it represented one of the values from the java.time.DayOfWeek enum in the new Java 8 Date and Time API. You could test whether dayOfWeek is Saturday with the following boolean expression:

${dayOfWeek == 'SATURDAY'}

The dayOfWeek variable here is converted to a String and compared to “SATURDAY.” This is unlike Java, where this conversion would never happen automatically. Although this is handy, it is certainly not type-safe. If you misspell Saturday (or if Saturday ever ceases being a day of the week) your IDE would probably not catch it, and if you compile JSPs during a continuous integration build to check for JSP compile-time errors, that would not catch it either. However, as of EL 3.0 you can use the static field access syntax to achieve type-safe enum constant reference. After all, enum constants are just public static final fields of their enum types:

${dayOfWeek == java.time.DayOfWeek.SATURDAY}

And, if you import DayOfWeek into your JSP, the expression is nearly as simple as the string-as-enum expression (and more like what you’d see in Java code):

${dayOfWeek == DayOfWeek.SATURDAY}

These last two techniques are type-safe and will be validated by your IDE and at compile time. Whichever you use is up to you, but we recommend a type-safe way.

Lambda Expressions

Expression Language 3.0 counts lambda expressions among its many new features. A lambda expression is an anonymous function that, typically, is passed as an argument to a higher-order function (such as a Java method). In the most general sense, lambda expressions are a list of parameter names (or some placeholder if the function has no parameters), followed by some type of operator, and finally the function body. In some languages supporting lambda expressions, this order is reversed or otherwise different. Lambda expression syntax in EL is nearly identical to that of Java 8 lambda expressions. The primary difference between the two is that in Java the body of a lambda expression can contain anything that’s legal in a Java method, whereas in EL the body of a lambda expression is another EL expression.

Just like with Java lambda expressions, EL lambda expressions use the arrow operator -> to separate the expression parameters on the left side from the expression in the right side. Also, again as with Java lambda expressions, the parentheses around the expression parameter are optional if there is exactly one parameter. The following expressions are valid EL lambda expressions:

a -> a + 5

(a, b) -> a + b

Of course, by themselves these lambda expressions are not complete EL expressions. Something must be done with the lambda expressions. They could be evaluated immediately:

${(a -> a + 5)(4)}

${((a, b) -> a + b)(4, 7)}

In the preceding EL expressions, the lambda expressions are declared and evaluated immediately. The resulting outputs of the two EL expressions are 9 and 11, respectively. Note that the lambda expression itself is surrounded by parentheses. This disambiguates the lambda expression from everything around it and enables you to execute it immediately. You could also define an EL lambda expression for use at a later time:

${v = (a, b) -> a + b; v(3, 15)}

The output of the second expression in this case is 18 because it executes the lambda expression defined before the semicolon. The lambda expression v can now be used in any other EL expression that follows this expression on the page. This is especially useful if the lambda is very complex.

Finally, you could also pass an EL lambda expression as an argument to a method called within an EL expression.

${users.stream().filter(u -> u.lastName == 'Williams' ||

u.lastName == 'Sanders ').toArray()}

Collections

Collections can be easily accessed in EL using the dot . and bracket [] operators. How you use the operators depends on what type of collection it is. Remember that in the Java Collections API, all collections are either Collections or Maps. Within the hierarchy of Maps, you simply have many different types of maps, all of which share a common foundation: Some key is associated with some value. The Collection hierarchy is a little more complicated. Within it you have Sets, Lists, and Queues. Because each type of collection has a different way in which you access its values, EL supports each one slightly differently.

Accessing values in a Map is quite simple and mimics the accessing of properties on JavaBeans. Suppose you have a Map named map with a key username mapped to the value “Jonathon” and a key userId mapped to the value “27.” You could access these two properties of the map using the bracket operators, as in the following example:

${map["username"]}

${map["userId"]}

However, this is not the only technique you can use to access the Map values. You could also treat the keys like bean properties and access their values using the dot operator:

${map.username}

${map.userId}

Although the second technique certainly involves fewer characters (always three fewer, to be exact), some people find the first technique more natural and more like how you would access Map values in languages that support operator overloading. You should use whichever you are more comfortable with. However, you should also note some restrictions on using the dot operator for accessing Map values. Simply put, if a key couldn’t be an identifier in Java, you must use brackets instead of the dot operator to access the value mapped to that key. This means that your key can’t contain spaces, periods, or hyphens, can’t start with a number, and can’t contain most special characters. (Although, there are a few surprising special characters that Java supports in identifiers, such as the dollar sign ($) and accented characters such as å, é, è, î, ö, ü, ñ, and so on.) If it contains any characters that aren’t valid in Java identifiers, you must use brackets. If you’re not sure, err on the side of caution and use brackets.

Accessing Lists is equally simple; however, it may surprise you just how forgiving it is. Consider a List (cleverly named list) with values “blue,” “red,” and “green,” in order from 0 to 2. You would access the values using the bracket operator just as if the List were actually an array. The following code demonstrates this.

${list[0]}

${list[1]}

${list[2]}

You cannot treat the List indexes as properties and access them with the dot operator. This results in syntax errors:

${list.0} <%-- The EL interpreter will complain about a syntax error --%>

However, EL does permit you to use string literals instead of numbers to index the List, just as if it were a Map with the List indexes serving as the keys:

${list["0"]}

${list['1']}

${list[2]}

The only rule when using string literals is that the strings must be convertible to integers; otherwise, your code results in runtime errors. Although this is certainly flexible, there is no reason to use string literals as List indexes, and doing so can result in other developers’ confusing your List (probably not named list) for a Map. We recommend using numeric literals instead.

The values of the other two types of collections, Sets and Queues, cannot be accessed using EL. These collections do not provide a means of directly accessing a value, such as an index with a List or a key with a Map. There are no “get” methods on Sets and Queues. You can access the values in these types of collections only by using iteration — something you explore in the next chapter. However, as with all types of collections, you can test whether Sets and Queues are empty using the empty operator:

${empty set}

${empty queue}

You can do more things with collections using EL collection streams, and you explore that more in the collection streams section later in this chapter.

USING SCOPED VARIABLES IN EL EXPRESSIONS

Expression Language’s sense of scoped variables and how variables are resolved makes it especially useful and powerful. Recall from Chapter 4 that JSPs have a set of implicit variables (request, response, session, out, application, config, pageContext, page, andexception) that you can use to obtain information from the request, session, and execution environment and affect the response. EL has a similar set of implicit variables; however, it also has an idea of implicit scope in which unknown variables are resolved. This enables you to obtain information from a variety of sources with minimal code. You explore these topics in this section.

For this section you use the User-Profile project available for download on the wrox.com code site. If you create it from scratch, be sure to create your web.xml file using the <jsp-config> from Chapter 4 and the <session-config> from Chapter 5, and create an index.jspwith the lone tag <c:redirect url="/profile" />.

The /WEB-INF/jsp/base.jspf file, which you have used in previous chapters, has changed slightly. Instead of just declaring the c tag library, it now also declares the fn function library, which you use in this section:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

A NOTE ABOUT SCOPES

There are four different attribute scopes mentioned in this section (page, request, session, and application), but you may not understand the difference between them or what exactly they are. Each of these scopes has a progressively larger and longer scope than the previous one. You should already be familiar with the request scope: It begins when the server receives the request and ends when the server completes sending the response back to the client. The request scope exists anywhere that has access to the request object, and the attributes bound to the request are no longer bound after the request completes.

In Chapter 5 you learned about sessions and session attributes, so by now you may have figured out that the session scope persists between requests, and that any code with access to the HttpSession object can access the session scope. When the session has been invalidated, its attributes are unbound and the scope ends.

The page and application scopes are somewhat different. The page scope encapsulates attributes for a particular page (JSP) and request. When a variable is bound to the page scope, it is available only to that JSP page and only during the life of therequest. Other JSPs and Servlets cannot access the page scope-bound variable, and when the request completes, the variable is unbound. With access to the JspContext or PageContext object, you can store and retrieve attributes that exist within the page scope using the setAttribute and getAttribute methods. The application scope is the broadest scope, existing across all requests, sessions, JSP pages, and Servlets. The ServletContext object you learned about in Chapter 3 represents the application scope, and attributes that are stored in it live in the application scope.

Using the Implicit EL Scope

The EL defines 11 implicit variables in the scope of EL expressions, and you will learn about them all later in this section. However, the implicit scope is more useful and more commonly used because of its capability to resolve an attribute in the request, session, page, or application scope. When an EL expression references a variable, the EL evaluator resolves the variable using the following procedure:

1. It checks if the variable is one of the 11 implicit variables.

2. If the variable is not one of the 11 implicit variables, the EL evaluator next looks for an attribute in the page scope (PageContext.getAttribute("variable")) that has the same name (case-sensitive) as the variable. If it finds a matching page scope attribute, it uses the attribute value as the variable’s value.

3. Finding no matching page attribute, the evaluator next looks for a request attribute (HttpServletRequest.getAttribute("variable")) with the same name as the variable and uses the attribute if it is found.

4. The evaluator looks for a session attribute (HttpSession.getAttribute("variable")) and uses it if found.

5. The evaluator looks for an application attribute (ServletContext.getAttribute("variable")) and uses it if found.

6. After the evaluator looks in all these places, if it finds no implicit variable or attribute matching the variable name, it raises an error.

The beauty of this feature is you do not need to retrieve an instance of the HttpServletRequest or HttpSession to use attributes on either of those objects. This is demonstrated in the ProfileServlet and profile.jsp file in the User-Profile project. Start by looking at thecom.wrox.User class, which has several private fields with matching accessor and mutator methods:

public class User

{

private long userId;

private String username;

private String firstName;

private String lastName;

private Map<String, Boolean> permissions = new Hashtable<>();

...

// mutators and accessors

...

}

This is a simple POJO that you can use to hold information about your “user.” You need to view this information somehow, so next create a very simple ProfileServlet:

@WebServlet(

name = "profileServlet",

urlPatterns = "/profile"

)

public class ProfileServlet extends HttpServlet

{

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException

{

User user = new User();

user.setUserId(19384L);

user.setUsername("Coder314");

user.setFirstName("John");

user.setLastName("Smith");

Hashtable<String, Boolean> permissions = new Hashtable<>();

permissions.put("user", true);

permissions.put("moderator", true);

permissions.put("admin", false);

user.setPermissions(permissions);

request.setAttribute("user", user);

request.getRequestDispatcher("/WEB-INF/jsp/view/profile.jsp")

.forward(request, response);

}

}

So far, you haven’t seen anything new. The Servlet creates a new User instance, sets some values on it, adds some permissions to it, creates a request attribute to hold the user object, and then forwards the request on to the view. The important code is contained in the/WEB-INF/jsp/view/profile.jsp file, which displays the user profile information in the browser:

<%--@elvariable id="user" type="com.wrox.User"--%>

<!DOCTYPE html>

<html>

<head>

<title>User Profile</title>

</head>

<body>

User ID: ${user.userId}<br />

Username: ${user.username} (${user.username.length()} characters)<br />

Full Name: ${fn:escapeXml(user.lastName) += ', '

+= fn:escapeXml(user.firstName)}

<br /><br />

<b>Permissions (${fn:length(user.permissions)})</b><br />

User: ${user.permissions["user"]}<br />

Moderator: ${user.permissions["moderator"]}<br />

Administrator: ${user.permissions["admin"]}<br />

</body>

</html>

There is a lot of interesting stuff in this JSP, and you will dissect it in a minute. For now, compile and fire up your debugger; then navigate to http://localhost:8080/user-profile/profile in your browser. You should see the page from the screen shot in Figure 6-1.

image

FIGURE 6-1

Now take a look at this JSP line by line to get a better understanding of how it works. First there’s the new, weird JSP comment at the top of the file:

<%--@elvariable id="user" type="com.wrox.User"--%>

This comment tag is not really needed, and in fact if you remove it, recompile, and rerun your application, it still works. (Go ahead; try it!) So what does it do? The special @elvariable comment is a convention that developers use to type-hint for their IDE. This comment tells the IDE “Yes, a user variable exists in the implicit scope on this page, and its type is com.wrox.User.” The advantage this gains you is that because the IDE knows the variable exists and what its type is, it can now provide auto-completion and intelligent suggestions that it could not otherwise provide. It can also validate that your EL expression is correct.

Even if you do not use an IDE or use one that does not support this convention, other developers maintaining your JSPs at a later date can quickly know what the EL variable type is. Your JSP-writing time will be much easier spent if you get in the habit of using@elvariable comments.

Next, you should notice the User ID line:

User ID: ${user.userId}<br />

Here, the user attribute that you added to the request in the Servlet code has been accessed as an EL variable using the implicit scope in the JSP page, and you have used the bean property userId instead of calling the accessor method directly. The line directly below it does the same with the username but also calls the length method on the username String.

Username: ${user.username} (${user.username.length()} characters)<br />

Note that instead of calling the length method directly you could have used the fn:length function, but that is used later in the code for a collection, and this serves as a good example of the alternative. Next, your JSP escapes the last and first name and concatenates them with a comma:

Full Name: ${fn:escapeXml(user.lastName) += ', '

+= fn:escapeXml(user.firstName)}

Note the use of the fn:escapeXml function to escape HTML characters that might be in the name and the += string concatenation operator to combine all the strings. The final part of your JSP prints out the user’s permissions:

<b>Permissions (${fn:length(user.permissions)})</b><br />

User: ${user.permissions["user"]}<br />

Moderator: ${user.permissions["moderator"]}<br />

Administrator: ${user.permissions["admin"]}<br />

The fn:length function outputs the number of elements in the user’s permissions collection, and the other three lines are all using the bracket operators to access values in the permissions Map.

As an exercise, edit your ProfileServlet and change the request.setAttribute("user", user) line to put the user on the session instead of the request:

request.getSession().setAttribute("user", user);

Now compile and rerun your application. You don’t need to make any changes to the JSP. The user attribute may be in a different scope (session instead of request) but is still in the implicit scope so that you can access it from EL expressions as an EL variable. When it was bound to the request, the user attribute existed until the request was complete, and then it was made eligible for garbage collection. Now that it is bound to the session, it is available to other requests from the same client, even if they go to different pages. However, this is not the only scope you could bind the user attribute to. Replace request.getSession() with this.getServletContext() and bind it to the application context:

this.getServletContext().setAttribute("user", user);

Now compile and rerun again without making any changes to the JSP. Again the user attribute was still in the implicit scope and accessible from your EL expression. You can access anything in the four supported scopes in this manner, which greatly simplifies your task of writing JSPs.

Using the Implicit EL Variables

As mentioned earlier in this section, there are 11 implicit EL variables available for use within EL expressions. With one exception, they are all Map objects. Most are used to access attributes from some scope, request parameters, or headers.

· pageContext is an instance of the PageContext class and is the only implicit EL variable that is not a Map. You should be familiar with PageContext from Chapter 4 and earlier in this section. Using this variable you can access the page error data and exception object (if applicable), the expression evaluator, the output writer, the JSP Servlet instance, the request and response, the ServletContext, the ServletConfig, and the session.

· pageScope is a Map<String, Object> containing all the attributes bound to the PageContext (page scope).

· requestScope is a Map<String, Object> of all the attributes bound to the ServletRequest. Using this, you can access these attributes without calling a method on the request object.

· sessionScope is also a Map<String, Object>, and it contains all the session attributes from the current session.

· applicationScope is the last of the scopes, a Map<String, Object> containing all the attributes bound to the ServletContext instance.

· param and paramValues are similar in that they both provided access to the request parameters. The param variable is a Map<String, String> and contains only the first value from any parameter with multiple values (similar to getParameter from ServletRequest), whereas the Map<String, String[]> paramValues contains all the values of every parameter (getParameterValues from ServletRequest). param is easier to use if you know a request parameter has only one value.

· header and headerValues provide access to the request headers, with Map<String, String> header containing only the first value of any multivalue headers and Map<String, String[]> headerValues containing all values for every header. Like param, header is easier to use if you know a header has only one value.

· initParam is a Map<String, String> containing all the context init parameters from the ServletContext instance for this application.

· cookie is a Map<String, javax.servlet.http.Cookie> containing all the cookies that the user’s browser sent along with the request. The keys in this map are the cookie names. It should be noted that it is possible to have two cookies with the same name (but different paths), and in that case this Map will contain only the first cookie with a given name in the order it existed in the request. This order might vary from one request to the next. There is no way in EL to access any of the other duplicate cookies with the same name without iterating over all the cookies. (Iteration with EL is something you learn how to do in the next chapter.)

To demonstrate the various EL implicit variables and how they can be used, create a file named info.jsp in the web root of your project and put the following code in it:

<%

application.setAttribute("appAttribute", "foo");

pageContext.setAttribute("pageAttribute", "bar");

session.setAttribute("sessionAttribute", "sand");

request.setAttribute("requestAttribute", "castle");

%>

<!DOCTYPE html>

<html>

<head>

<title>Information</title>

</head>

<body>

Remote Address: ${pageContext.request.remoteAddr}<br />

Request URL: ${pageContext.request.requestURL}<br />

Session ID: ${pageContext.request.session.id}<br />

Application Scope: ${applicationScope["appAttribute"]}<br />

Page Scope: ${pageScope["pageAttribute"]}<br />

Session Scope: ${sessionScope["sessionAttribute"]}<br />

Request Scope: ${requestScope["requestAttribute"]}<br />

User Parameter: ${param["user"]}<br />

Color Multi-Param: ${fn:join(paramValues["colors"], ', ')}<br />

Accept Header: ${header["Accept"]}<br />

Session ID Cookie Value: ${cookie["JSESSIONID"].value}<br />

</body>

</html>

The first four lines of the JSP set attributes within the various scopes, for the purposes of demonstration. The lines within the HTML body print out varying information about the request, attributes on the different scopes, parameters in the URL, headers, and cookies. Compile and debug your application; then go to http://localhost:8080/user-profile/info.jsp?user=jack&colors=green&colors=red in your browser. You should see a good deal of information printed out to the screen. If the Session ID Cookie Value line is empty, this means that your session was just created and the browser did not send a cookie yet; refresh the page and a value should appear here.

One last JSP demonstrates the priority of the various scopes when resolving variables in the implicit EL scope. Create a file named scope.jsp in the web root, and put the following code in it.

<%

pageContext.setAttribute("a", "page");

request.setAttribute("a", "request");

session.setAttribute("a", "session");

application.setAttribute("a", "application");

request.setAttribute("b", "request");

session.setAttribute("b", "session");

application.setAttribute("b", "application");

session.setAttribute("c", "session");

application.setAttribute("c", "application");

application.setAttribute("d", "application");

%>

<!DOCTYPE html>

<html>

<head>

<title>Scope Demonstration</title>

</head>

<body>

a = ${a}<br />

b = ${b}<br />

c = ${c}<br />

d = ${d}<br />

</body>

</html>

The majority of this JSP is setup code, with only four EL expressions making up the demonstration. The a attribute has conflicting values in all four scopes, b in three, and c in two. The d attribute is present only in the application scope. The value displayed next to each name on the page will be the name of the scope with the highest precedence among the scopes with conflicting values. Compile and run your application and navigate to http://localhost:8080/user-profile/scope.jsp. The output should look identical to what follows, indicating that the EL engine looks for implicitly scoped variables first in the page scope and then in the request, session, and application scopes, in that order.

a = page

b = request

c = session

d = application

ACCESSING COLLECTIONS WITH THE STREAM API

One of the biggest additions to Expression Language 3.0 in Java EE 7 is support for the Collections Stream API introduced in Java SE 8. Because the API is supported natively in EL 3.0, you do not need to run your application in Java 8 to take advantage of this new EL feature. In this section, you learn the basics of the Stream API and how to use it in your JSPs.

NOTE In an early, prerelease version of Expression Language 3.0, the specification included an implementation of Microsoft LINQ (Language Integrated Query). This added collection-querying capabilities using the LINQ standard query operators. The final specification was rewritten to remove the LINQ features and replace them with an equivalent to the Stream API. This provides consistency across the Java language and Expression Language specifications.

The basis of the Stream API is the no-argument stream method present on every Collection. This method returns a java.util.stream.Stream that can filter and otherwise manipulate a copy of the collection. The java.util.Arrays class also provides many static methods for retrieving Streams from various arrays. Using this Stream, you can perform many different operations. Some of these operations return other Streams, allowing you to create a chained pipeline of operations. This pipeline consists of a pipeline source (the Stream), theintermediate operations (such as filtering and sorting), and finally a terminal operation (such as converting the results to a List that can be iterated and displayed).

In EL 3.0, you can call the stream method on any EL variable that is a Java array or a Collection. The returned Stream isn’t actually a java.util.stream.Stream because EL 3.0 must work in Java 7, where Streams do not exist yet. Instead, the returned Stream is an EL-specific implementation of the Stream API. For example, the following EL expression filters a Collection of books by title, reduces the properties available for each book to just the title and author, and returns a List of the results:

books.stream().filter(b->b.title == 'Professional Java for Web Applications')

.map(b->{ 'title':b.title, 'author':b.author })

.toList()

Understanding Intermediate Operations

As mentioned earlier, intermediate operations filter, sort, reduce, transform, or otherwise alter a collection of values so that the collection ends up in the desired state. It’s important to understand that when performing intermediate operations on a Stream, the original Collection or array is never altered. The operations affect only the contents of the stream. You’ll find many different intermediate operations, and you learn about the most common and useful ones in this section. You can learn about the rest of them by downloading and reading the JSR 341 specification PDF from the specification download page.

Filtering the Stream

The filter operation is probably the operation you will use most often. It filters the contents of the Stream, typically reducing the number of objects contained therein. The filter operation accepts a predicate argument — a lambda expression that returns a booleanand accepts a single argument whose type is the element type of the Stream. Given a List<E> where E is the element type, stream returns a Stream<E>. Calling filter on this Stream<E>, you supply a Predicate<E> with the signature E -> boolean. You then use properties of Eto determine whether to include that particular E in the resulting Stream<E>. To better understand this, consider the following expression:

${books.stream().filter(b -> b.author == "John F. Smith")}

The predicate in this case is the lambda expression that accepts a book as an argument and tests whether the book’s author is John F. Smith. When passed to the filter operation, the predicate applies to every book in the Stream, and the resulting Stream contains only those books for which the predicate returns true.

You can also use the special distinct operation to filter out duplicate values. The following expression removes the duplicate 3s and 5s from the List:

${[1, 2, 3, 3, 4, 5, 5, 5, 5, 6].stream().distinct()}

Manipulating Values

You can manipulate the values in a Stream using the forEach operation. Like filter, forEach accepts a lambda expression that is evaluated for every element in the Stream. However, this lambda expression is a consumer, meaning it has no return value. You can use this to manipulate the values in the Stream, likely to transform them in some way. Here is one potential use case:

${books.stream().forEach(b -> b.setLastViewed(Instant.now()))}

Sorting the Stream

You sort the stream using the sorted operation. For a Stream<E>, the sorted operation accepts a java.util.Comparator<E>. As a Java developer, you are probably familiar with this interface, which can be represented with the lambda expression (E, E) -> int. This lambda expression or Comparator compares two elements in the Stream using an efficient sorting algorithm that is unspecified and implementation-specific. The following expression sorts books by their title:

${books.stream().sorted((b1, b2) -> b1.title.compareTo(b2.title))}

A variation of the sorted operation exists that does not accept any arguments. Instead, it assumes that the elements in the Stream implement the java.lang.Comparable interface, meaning you can naturally sort them. The following naturally orders the list of numbers from least to greatest. The resulting list is -2, 0, 3, 5, 7, 8, 19.

${[8, 3, 19, 5, 7, -2, 0].stream().sorted()}.

Limiting the Stream Size

You can limit the number of elements in the Stream using the limit and substream operations. Use limit to simply truncate the Stream after the specified number of elements. substream is more useful for pagination because you can specify a start index (inclusive) and end index (exclusive).

${books.stream().limit(10)}

${books.stream().substream(10, 20)}

Transforming the Stream

Using the map operation, you can transform the elements in the Stream to some other type of element. The map operation accepts a mapper that expects one type of element and returns a number. Given a Stream<S>, map expects a lambda expression whose sole argument is of type S. If the lambda expression then returns a different type R, the resulting Stream is a Stream<R>. The following takes a List<Book>, retrieves a Stream<Book>, and transforms it into a Stream<String> containing only the book titles:

${books.stream().map(b -> b.title)}

Of course, you can return more complex types. You might have a different type, DisplayableBook, with a limited set of properties. Or you could create an implicit List or Map, returning a Stream<List<Object>> or Stream<Map<Object, Object>>:

${books.stream().map(b -> [b.title, b.author])}

${books.stream().map(b -> {"title":b.title, "author":b.author})}

Using Terminal Operations

After you filter, sort, or otherwise transform your Stream, you need to perform some final operation that converts the Stream back into a useful value, Collection, or array. This type of operation is a terminal operation. It is terminal because unlike intermediate operations, which all return Streams that can be further acted on, this operation does not return a Stream. It evaluates any intermediate operations deferred for performance reasons and then converts the final result as desired. Ultimately, you must always perform a terminal operation. A Stream is not very useful by itself; you need a final value to act on.

Returning a Collection

You can use the toArray and toList operations to return a Java array or List of the final result element type. For example, the following expressions return a String[] and List<String> of book titles, respectively:

${books.stream().map(b -> b.title).toArray()}

${books.stream().map(b -> b.title).toList()}

If you performed any sorted intermediate operations on the Stream, the resulting array or List will be in the order indicated with those operations. You can also use the iterator operation to return a suitable java.util.Iterator.

Using Aggregate Functions

You can aggregate the values in the Stream using the min, max, average, sum, and count operations. The count operation can operate on any type of Stream, whereas the average and sum operations require the final Stream element types to be coercible to Numbers. countreturns the number of elements in the Stream as a long; average returns the average of all the Stream elements as an Optional<? extends Number>; and sum returns the sum of all the Stream elements as a Number. An Optional is a placeholder that can report whether the returned value was null and provide the returned value when requested.

The min and max operations are both interesting. They both return Optional<E> where E is the element type of the resulting Stream. Without any arguments, these operations require that the Stream elements implement Comparable. However, you can provide a Comparatorargument to these operations when wanted.

The following expressions represent some common use cases for these aggregating terminal operations:

${books.stream().map(b -> b.price()).min()}

${books.stream().map(b -> b.price()).max()}

${books.stream().filter(b -> b.author == "John F. Smith")

.map(b -> b.price()).average()}

${books.stream().filter(b -> b.author == "John F. Smith").count()}

${cartItems.stream().map(i -> i.price() * i.quantity()).sum()}

Returning the First Value

You can use the findFirst operation to return the first element in the resulting Stream. For a Stream<E> it returns an Optional<E> because the Stream might be empty, meaning there is no first element to return.

${books.stream().filter(b -> b.author == "John F. Smith").findFirst()}

Putting the Stream API to Use

For a simple exercise in the use of the Stream API, you add a JSP to the User-Profile project and filter, map, and sort a List of Users. Start by adding a constructor to the User object (and also adding a default constructor so that previous code won’t break).

public User() { }

public User(long userId, String username, String firstName, String lastName)

{

this.userId = userId;

this.username = username;

this.firstName = firstName;

this.lastName = lastName;

}

Now create a collections.jsp file in the web root of the project and put the following code in it:

<%@ page import="com.wrox.User" %>

<%@ page import="java.util.ArrayList" %>

<%

ArrayList<User> users = new ArrayList<>();

users.add(new User(19384L, "Coder314", "John", "Smith"));

users.add(new User(19383L, "geek12", "Joe", "Smith"));

users.add(new User(19382L, "jack123", "Jack", "Johnson"));

users.add(new User(19385L, "farmer-dude", "Adam", "Fisher"));

request.setAttribute("users", users);

%>

<!DOCTYPE html>

<html>

<head>

<title>Collections and Streams</title>

</head>

<body>

${users.stream()

.filter(u -> fn:contains(u.username, '1'))

.sorted((u1, u2) -> (x = u1.lastName.compareTo(u2.lastName);

x == 0 ? u1.firstName.compareTo(u2.firstName) : x))

.map(u -> {'username':u.username, 'first':u.firstName,

'last':u.lastName})

.toList()}

</body>

</html>

The setup code at the top of the file creates some users and adds them to the list. Then the EL expression filters the list to users whose usernames contain the number 1; orders by the last name and then first name; selects the username, first name, and last name from each matching user; and then evaluates immediately to a List. Finally, the List is automatically coerced to a String for display on the screen (using the List’s toString method). Notice the use of the semicolon and assignment (=) operators in the sorted lambda expression — this allows you to compare the last names only once, assign the comparison to a variable (x), and then test the value of x, returning it if the last names are different and comparing the first names if the last names are the same. The body of the sortedlambda expression is surrounded by parentheses (in bold) because the lambda operator (->) has a higher precedence than the assignment and semicolon operators.

You can test this out by compiling and running your application and going to http://localhost:8080/user-profile/collections.jsp in your browser.

NOTE In Chapter 4 you explored using Java code in JSPs and learned about some of the many reasons using Java within JSPs is discouraged. The introduction of the Stream API to the Expression Language provides a lot of additional power for the JSP author to manipulate collections significantly. If you get in the habit of using the Stream API in JSPs routinely, you may find that you have started putting business logic in the presentation layer instead of the Java code. Only you can decide whether this is appropriate for your needs, but it is something to keep in mind. You will not see use of the Stream API in JSPs anywhere else in the book — these types of operations are performed only in the Java code from now on.

REPLACING JAVA CODE WITH EXPRESSION LANGUAGE

In this section you begin replacing some of the Java code in your JSPs in the ongoing customer support application with EL expressions. The Customer-Support-v4 project on the wrox.com code download site contains these changes. You cannot replace all the Java code yet. For that, we will need the next chapter. Start by updating your /WEB-INF/jsp/base.jspf file to contain a tag library declaration for the JSTL function library:

<%@ page import="com.wrox.Ticket, com.wrox.Attachment" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

You do not need to make any changes to Java code in this section. Everything you change will be within JSPs. The ticketForm.jsp view in /WEB-INF/jsp/view is already devoid of any Java code, so there is nothing you can do to improve that. viewTicket.jsp, on the other hand, has several things that can be replaced. The new code for this file is in Listing 6-1.

Notice that the new code has @elvariable type hints at the top for ticketId and ticket, and that the ticketId Java variable has been removed. The ticket Java variable has not been removed, however, because EL expressions cannot replace everything the ticketvariable is being used for — such as iterating over the attachments. The new EL expressions have been highlighted in bold.

LISTING 6-1: viewTicket.jsp

<%--@elvariable id="ticketId" type="java.lang.String"--%>

<%--@elvariable id="ticket" type="com.wrox.Ticket"--%>

<%

Ticket ticket = (Ticket)request.getAttribute("ticket");

%>

<!DOCTYPE html>

<html>

<head>

<title>Customer Support</title>

</head>

<body>

<a href="<c:url value="/login?logout" />">Logout</a>

<h2>Ticket #${ticketId}: ${ticket.subject}</h2>

<i>Customer Name - ${ticket.customerName}</i><br /><br />

${ticket.body}<br /><br />

<%

if(ticket.getNumberOfAttachments() > 0)

{

%>Attachments: <%

int i = 0;

for(Attachment a : ticket.getAttachments())

{

if(i++ > 0)

out.print(", ");

%><a href="<c:url value="/tickets">

<c:param name="action" value="download" />

<c:param name="ticketId" value="${ticketId}" />

<c:param name="attachment" value="<%= a.getName() %>" />

</c:url>"><%= a.getName() %></a><%

}

%><br /><br /><%

}

%>

<a href="<c:url value="/tickets" />">Return to list tickets</a>

</body>

</html>

The /WEB-INF/jsp/view/sessions.jsp file is another JSP that could use EL expressions. You can find the new code for this file in Listing 6-2. The only changes to this JSP are the @elvariable type hint and the lone EL expression in bold. None of the rest of the Java code can be replaced at this time because of the need for recursion and formatting the time interval.

LISTING 6-2: sessions.jsp

<%--@elvariable id="numberOfSessions" type="java.lang.Integer"--%>

<%@ page import="java.util.List" %>

<%!

private static String toString(long timeInterval)

{

if(timeInterval < 1_000)

return "less than one second";

if(timeInterval < 60_000)

return (timeInterval / 1_000) + " seconds";

return "about " + (timeInterval / 60_000) + " minutes";

}

%>

<%

@SuppressWarnings("unchecked")

List<HttpSession> sessions =

(List<HttpSession>)request.getAttribute("sessionList");

%>

<!DOCTYPE html>

<html>

<head>

<title>Customer Support</title>

</head>

<body>

<a href="<c:url value="/login?logout" />">Logout</a>

<h2>Sessions</h2>

There are a total of ${numberOfSessions} active sessions in this

application.<br /><br />

<%

long timestamp = System.currentTimeMillis();

for(HttpSession aSession : sessions)

{

out.print(aSession.getId() + " - " +

aSession.getAttribute("username"));

if(aSession.getId().equals(session.getId()))

out.print(" (you)");

out.print(" - last active " +

toString(timestamp - aSession.getLastAccessedTime()));

out.println(" ago<br />");

}

%>

</body>

</html>

Now compile and run the customer support application and go to http://localhost:8080/support/ in your browser. Log in, create a few tickets, and view the tickets. Go to http://localhost:8080/support/sessions and view the list of sessions. Everything should work the same way it did in Chapter 5, but now EL takes care of some of your output.

SUMMARY

In this chapter you learned about the history of the Java Unified Expression Language, the basic EL syntax, and what EL expressions are used for. You explored reserved words, operators, literal values, accessing object properties and methods, EL functions and the JSTL function library, static field and method access, enums, lambda expressions, and collections operators. You were introduced to the four different scopes and the implicit EL scope, and learned about the eleven implicit EL variables. You also learned about the Stream API and its addition to EL 3.0. Finally, you replaced some Java code with EL expressions in the customer support application that you started in Chapter 3.

It should be clear by now that although EL expressions can replace a lot of Java code, they do not do everything you need to replace all the Java code in your JSPs. For example, you cannot loop within EL expressions or have blocks of code evaluated based on whether some expression is true. For this you need the Java Standard Tag Library, which you explore in the next chapter.