In Chapter 2, you learned that there are three types of JSP elements: scripting, directives, and actions. I described the first two types directly in Chapter 2, and the time has come to look at JSP actions. Actions, like scriptlets, are processed when a page is requested. In this chapter, you will learn how to use JSP standard actions, how to create actions of your own design, and how to use some of the actions contained in the JSP Standard Tag Library. Besides small specific examples, you will also learn the role of actions in the eshop application that I introduced in the previous chapter. Actions can do everything that scripting elements can do, as you will see at the end of the next chapter, when I will tell you how to write JSP code without any scripting element at all.
JSP Standard Actions
While Tomcat executes directive elements when translating a page, it executes action elements when processing a client’s HTTP request.
There are eight JSP standard actions (forward, include, useBean, setProperty, getProperty, element, text, and plugin) and five additional actions that can only appear in the body of other actions (param, params, attribute, body, and fallback). There are also two additional action elements—invoke and doBody—that you cannot invoke from within JSP pages. There is also a further standard action—root—that I will explain at the end of the next chapter.
Actions: forward, include, and param
The include action is similar to forward, the main difference being that it returns control to the including page after the included page has completed execution. The output of the included page is appended to the output generated by the including page up to the point where the action is executed.
As shown in the example, jsp:param lets you define a new parameter for the invoked page, which also has access to the parameters already available to the invoking page.
Tomcat clears the output buffer upon executing the forward action. Therefore, the HTML code generated up to that point by the current page is lost. But if the current page has already filled the response buffer by the time it is aborted with forward, that part of the response will have already left the server. This will probably result in a bad page sent to the client. Therefore, you have to be very careful when invoking forward from within a page that generates a large output.
You don’t have to worry about such a problem with include, because Tomcat doesn’t clear the output buffer when it executes that action.
TopMenu.jsp
HTML Generated by TopMenu.jsp
Notice that TopMenu.jsp uses styles (such as class="header") that aren’t loaded or defined within the same file. If you’re wondering how that’s possible, you probably don’t clearly understand the distinction between source JSP and output HTML. The JSP code in TopMenu.jsp is executed on the server, and it produces HTML code, which is then appended to the output buffer. JSP doesn’t need style sheets. It is the generated HTML that needs them when it’s interpreted by the client’s browser.
You might think that <jsp:include page="..."/> is the same as <%@include file="..."%>, but this is definitely not the case. The most important difference is that while the include directive includes the content of a file without any processing, the include action includes the output of the included resource. If the resource is a JSP page, this makes a big difference. In practical terms, this also explains why JSP pages to be included with jsp:include must be well-formed and complete pages rather than simply JSP fragments.
To illustrate a subtle consequence of the different mechanisms of inclusion, I have prepared a small test page (see Listing 6-3). To try it out, you can copy to the tests Tomcat folder (webappsROOT ests) the folder named jsp_include that you will find in the software package for this chapter, or better, you can create a new web project “tests” in Eclipse and copy the same jsp_include folder in the WebContent folder (you can do this by simply copying the folder from the software package and pasting it in the folder in Eclipse environment).
includes.jsp
As you can see, includes.jsp displays the letter B in all cases except when you implement the outer inclusion with the directive and the inner inclusion with the action. This means that only with that particular combination of file inclusions, includes.jsp accesses the c.txt file that is in the same directory. In the other three cases, includes.jsp accesses the c.txt file that is in the d directory, together with b_act.jsp and b_dir.jsp. To understand these results, you have to know that when Tomcat translates a JSP page into a Java class, it replaces <jsp:include page="fname"/> with an execution of the method org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "fname", out, false), while <%@include file="fname"%> results in the copying of the content of the fname file. Therefore, in the third case of the example, the <jsp:include page="c.txt"/> inside b_act.jsp is replaced with an include(request, response, "c.txt", out, false), and then the whole b_act.jsp is copied into includes.jsp. That’s why the servlet picks up the file in the directory of includes.jsp. The fact that b_act.jsp was in a different directory was lost when its include directive was replaced by the file content.
I decided to spend a bit of time on this issue because the inclusion mechanism is often misunderstood and causes many people to knock their heads against the wall when files seem to disappear.
Action: useBean
The result is stored in an object of type book, from which JSP can obtain individual book attributes by executing simple get methods such as book.getTitle() and book.getAuthor().
jsp:useBean accepts the attributes beanName, class, id, scope, and type, of which only id is mandatory.
If you type <jsp:useBean id="objName"/>, Tomcat will check whether an object named objName exists in pageContext. If it exists, Tomcat will create a variable named objName of the same type as the object, so that you can access the object in subsequent JSP scripting elements. If the object doesn’t exist, Tomcat will throw a java.lang.InstantiationException.
If you type <jsp:useBean id="objName" scope="aScope"/> with aScope set to one of the words page, request, session, or application, Tomcat will behave as described in the previous paragraph, but it will look for the objName object in the given scope rather than in the page context. In other words, page is the default scope.
Also jsp:useBean can create new objects. Whether useBean does it and what type of variable it makes available for JSP scripting depends on the three remaining attributes: class, type, and beanName.
If you specify class and set it to a fully qualified class name (i.e., with its package, as in java.lang.String) but specify neither type nor beanName, Tomcat will instantiate an object of the given class in the scope you specify with the attribute scope (or in the page scope by default).
If together with class you also specify type, Tomcat will set the data type of the new object to the value of the type attribute. You can set the type attribute to the same class as the class attribute (which is equivalent to omitting type), to a superclass of class, or to an interface implemented by class.
If instead of class you specify the beanName attribute, Tomcat will behave as if you had specified class, but only after attempting to find a serialized bean of that class. Serializing a bean means that the object’s data is converted to a byte stream and saved in a file with the extension ser. Tomcat expects to find serialized objects in the same folder containing the application classes. For example, a serialized bean of the xxx.yyy.Zzz class is expected to be in the WEB-INFclassesxxxyyyzz.ser file. This mechanism lets you save an object in a file and then load it into your JSP page. You can actually have several serialized beans of the same class (e.g., Zzz.ser, Zzz_tests.ser, Zzz25.ser, and Abc.ser). Fortunately, the designers of JSP have thought this issue through and allowed you to set the value of beanName at request time (the other attributes must be hard-coded), so that you can parameterize your page for what concerns loading serialized objects.
Finally, pay attention that the servlet creates the objects in the correct scope. In the previous chapter, the initialization method of the eshop servlet (see Listing 5-1) first instantiated the dataManager object and then saved it in the application scope.
Don’t confuse the scope of a bean as specified with the useBean attribute scope with the scope of the scripting variable that Tomcat associates to the bean.
However, the scope of the scripting variable myObj is determined by where within your page you execute useBean , as with the declaration of any other scripting variable. If you find this confusing, consider this: in the page containing the second useBean, you don’t have access to the scripting variable myObj until you execute the useBean action. Before that, the scripting variable is undefined, although the bean called myObj already exists, as it was instantiated by the first useBean in a previously executed page. This tells you that the scripting variable referring to the object and the actual object are two different things with two different scopes, even if they share the same name.
This representation should make completely clear that the object and the scripting variable are two different entities. In the second scriptlet, you could even decide to call the scripting variable with a different name.
Because of all the options implemented by combining its attributes, as I said at the beginning, useBean is somewhat tricky to use. But you can always come back to this page in case of doubt!
Actions: setProperty and getProperty
A bean property is nothing else than an attribute of a bean’s class, but only when you define for that attribute the standard get and set methods. To make it completely clear, both get and set must be there. Otherwise, that class attribute is not a bean property.
Additionally, you must name the two methods respectively get and set, followed by the full name of the attribute with the first letter capitalized. For example, if you define the attribute named myAttr, you must name the two attributes getMyAttr and setMyAttr. Otherwise, again, Tomcat will not recognize the attribute as a bean property.
Customer.java
As you can see, the Customer class defines private attributes and then the methods to access them, so that they can be recognized as properties.
The implementation with the action is more compact and, most importantly, it remains valid regardless of whether you add or remove customer attributes. And that’s what makes setProperty worthwhile.
MyClass.java
myObj.jsp
To try it out, create a new class “MyClass” in the created Eclipse project “tests” unsder the package tests.myclasses and then copy the code in the folder named jsp_getProperty that you will find in the software package for this chapter. Do the same thing for the JSP page. Type in a browser http://localhost:8080/tests/myObj.jsp.
As you can see, myObj.jsp instantiates the bean object with useBean and initializes its attribute by executing setProperty within the body of useBean. The advantage of doing it that way is that Tomcat only attempts to execute the sub-action setProperty if the instantiation of the bean succeeds.
The example also shows that in setProperty you can replace the value attribute with param. Then, Tomcat sets the attribute to the value of the identically named request parameter. Notice how the page directive with trimDirectiveWhitespaces set to true only leaves a single newline, after <body>, because it is in the HTML template. It results in 11 and 22 being “fused” into 1122. Not necessarily what you would like to have.
Actions: element, attribute, and body
With the actions element, attribute, and body , you can define XML elements dynamically within a JSP page. One reason why you might like to define XML elements dynamically is that your JSP page, instead of generating a web page to be displayed in a browser, might need to generate an XML file used to exchange data with other modules and applications. The word dynamically is important, because it means that you can generate the XML elements at request time rather than statically at compile time.
actel_element_attribute.jsp
The Output of actel_element_attribute.jsp
You can test it at http://localhost:8080/tests/actel_element_attribute.jsp.
The second highlight shows how you can use attribute to move the page attribute of include to be inside the body of the include action. The content of the file text.txt is unimportant. You’ll find a one-line file in the jsp_element folder of the software package for this chapter (you can copy it in the Eclipse project named “tests”).
Action: text
Its body cannot contain other elements; it can only contain text and EL expressions.
Actions: plugin, params, and fallback
Here the type specifies either an object or a bean, code specifies the class name of the object, and at last the codebase specifies the URL containing the files of the class. The syntax shows also how to pass params and how to inform the user with fallback if the object fails to start.
Comments and Escape Characters
They can also span over several lines.
Regular HTML comments such as <!-- ... --> won’t work with JSP.
JSP comments have the advantage over HTML comments in that they are not sent to the client. Their content is therefore invisible to the user.
To include the sequence of characters <% and %> in template text, you have to “break” them with a backslash, like in <\% and %>, so that the JSP engine doesn't interpret them as the beginning and end of scripting elements. Alternatively, you can replace the inequality signs with their corresponding HTML entities, as in <% and %>.
JSP’s Tag Extension Mechanism
You can define your own actions to replace lengthy scriptlets. By “hiding” functions behind custom tags, you can increase modularity and maintainability of your pages.
- 1.
Define Java classes that provide the functionality of the new actions, including the definition of their attributes (e.g., myAttributeName). These classes are called tag handlers .
- 2.
Provide a formalized description of your action elements, so that Tomcat knows how to handle them. For example, you need to specify which actions can have a body and which attributes can be omitted. Such a description is called a tag library descriptor (TLD).
- 3.
In the JSP pages, tell Tomcat that the pages need your tag library and specify the prefix that you want to identify those custom tags with.
I will take you through these steps, beginning with bodyless actions, which are simpler to implement.
Bodyless Custom Actions
with the date attribute accepting values in the form yyyy-mm-dd and defaulting to the current date. All the examples of this section on bodyless actions and the following section of bodied actions are in the software package for this chapter. To test them, import the project tldtest in Eclipse.
Step 1: Define the Tag Handler
A tag handler for a bodyless custom tag is a class that implements the interfaces java.io.Serializable and javax.servlet.jsp.tagext.Tag. Remember that to satisfy an interface, you have to implement all the methods it defines.
The Methods of the Tag Interface
Method | Description |
---|---|
int doEndTag() | Processes the end tag |
int doStartTag() | Processes the start tag |
Tag getParent() | Provides a reference to the closest enclosing tag handler |
void release() | Removes all the references to objects |
void setPageContext(PageContext pc) | Sets the current page context |
void setParent(Tag t) | Sets the closest enclosing tag handler |
Fortunately, the javax.servlet.jsp.tagext.TagSupport class makes life easier by implementing the Tag interface with default methods and other useful methods. Therefore, you only need to extend TagSupport and overwrite the methods you need for your weekday action. You certainly don’t need getParent, because the action isn’t going to be used in the body of other actions. You don’t need doStartTag either, because the action is bodyless, and, as a consequence, you don’t have separate start and end tags. In conclusion, you only need to overwrite doEndTag with a method containing all the functionality of the weekday tag.
WeekdayTag.java
You need the setDate method because Tomcat uses it to pass the value of the action’s date attribute to the tag handler. The corresponding getDate method isn’t present, because it is never used and can be omitted. That said, you might argue that working with incomplete Java beans, sooner or later, will get you into trouble. If the action is executed without the date attribute, the date variable defined in doEndTag remains set to null, and the calendar cal, which is used to determine the day of the week, remains set to the current date. On the other hand, if a date attribute is specified in the action, its value is parsed and used to set the calendar.
Notice that the tag handler is named like the tag but with the first letter capitalized and with the Tag suffix. This is a good practice to follow, although you can name your handlers whatever you like. You’ll see in a moment how to make the association between a tag and its handler.
The return value EVAL_PAGE means that execution should continue with the page code following the custom action. Use SKIP_PAGE to abort the page.
Step 2: Define the TLD
wow.tld
As you can see, the outermost element is taglib, which contains a tag element for each custom action (in this case, only weekday). Apart from tag, all taglib sub-elements in the example are for information purposes or to be used by tools and can be omitted.
The tag element contains an attribute sub-element for each action attribute (in this case, only date). Of the tag sub-elements in the example, you can omit description and display name . The sub-element name defines the custom action name; tag class specifies the fully qualified class name of the tag handler; and body content specifies the action to be bodyless.
The sub-element tag class is what gives you the freedom to name your tag handlers anything you like. The sub-element body content is mandatory and can only have one of the following three values: empty, scriptless, or tagdependent. The value scriptless is the default and means that the body cannot contain scripting elements, while EL expressions and JSP actions are accepted and processed normally. The value tagdependent means that the body content is passed to the tag handler as it is, without any processing. This is useful if the body contains character sequences, such as <%, that would confuse Tomcat.
The attribute element in the example has three sub-elements: name, which sets the action attribute name; type, which sets the class name of the attribute value; and rtexprvalue, which decides whether the attribute accepts values at request time.
When processing the start tag of the custom action, Tomcat would have parsed the string passed to the action (as in num="23") to obtain the Integer value for the tag handler.
If you had omitted the rtexprvalue sub-element or set it to false, you would have been forced to pass to the date attribute only constant values, such as "2007-12-05", instead of runtime values such as "<%=aDate%>" (rtexpr stands for real-time expression).
Inside WEB-INF, create a folder named tlds and place wow.tld there.
Step 3: Use the Custom Action
weekday.jsp
Line 2 contains the taglib directive, line 4 uses weekday without the date attribute, and line 6 passes the request parameter d to the action. It’s as simple as that.
If you type in your browser http://localhost:8080/tldtest/weekday.jsp?d=2012-12-25, you get two lines, such as Today: Wed and 2012-12-25: Tue. If you type the URL without the query, the second line of the output becomes null: Wed. You can execute the example by typing in your browser http://localhost:8080/tldtest/weekday.jsp.
On the other hand, if you type a query with a bad date, such as d=2012-1225, Tomcat shows you an error page with a back trace that begins as follows:
You can execute the example by typing in your browser http://localhost:8080/tldtest/weekday.jsp?d=2012-1225.
Bodied Custom Actions
Step 1: Define the Tag Handler
Similar to bodyless actions, the tag handlers for bodied actions need to implement an interface, only this time it’s javax.servlet.jsp.tagex.BodyTag instead of Tag. Again, similarly to bodyless actions, the API provides a convenient class that you can use as a basis: javax.servlet.jsp.tagext.BodyTagSupport. However, as opposed to what you did in the tag handler for a bodyless action, you cannot simply replace the doEndTag method, because the action body will have come and gone by the time you reach the end tag. You first have to overwrite doAfterBody.
the method doAfterBody won’t be executed at all. How can you then print out the default day?
WeekdayBodyTag.java
Lines 1, 4, and 5 implement the mechanism to ensure that you write the default date but only when the body is empty. In line 1, you define a boolean instance variable called bodyless and set it to true. If there is no body to process, doAfterBody does not run, and doEndTag in line 5 prints the default day of the week. If, on the other hand, there is a body to process, doAfterBody in line 4 sets bodyless to false, and doEndTag does nothing.
Line 2 shows you how to get the body content and line 3 how to get the method to print the date while processing the body. The method has been named getPreviousOut to remind you that there can be actions within actions, in which case you’ll want to append the output of an inner action to that of an outer one.
Step 2: Define the TLD
The tag Element for weekdayBody
Notice that you define the body-content sub-element as scriptless even though it is the default. The purpose is to make the code more readable. It’s just a matter of taste.
Step 3: Use the Custom Action
weekday_b.jsp for the Bodied Action
Notice that I replaced the request.getParameter("d") logic with the simpler and more elegant EL expression ${param.d}. You have to use an EL expression in any case, because scripting elements aren’t allowed in the body of an action. Therefore, you couldn’t have used <%=d%>. You will learn how to use EL in the next section of this chapter.
Tag Files
Tag files are special JSP files that replace tag handlers written in Java. After all, JSP basically is Java.
Do you remember when I told you in Chapter 2 that the only available directive elements are page, include, and taglib? Well, I lied. There are three more directives: tag, attribute, and variable. The reason I didn’t mention them is that you can only use them in tag files. Now that you know how to develop custom-tag libraries with Java, I can tell you how to develop them using the JSP syntax and the newly revealed directives. The examples of this section are in the folder tagtest of the software package for this chapter. To install them, import the package into Eclipse.
Bodyless Tag
weekday.tag
The tag directive of a tag file replaces the page directive of a JSP page, and the attribute directive lets you define an input parameter. As Tomcat handles the tag exceptions for us, I removed the try/catch constructs, which certainly makes the code more readable. Another simplification is in sending the result to the output, because in the tag file the implicit variable out makes it unnecessary to invoke pageContext.getOut().
weekday_t.jsp
As you can see, the only difference is that the attribute uri="/WEB-INF/tlds/wow.tld" of the taglib directive has become tagdir="/WEB-INF/tags".
wow.tld for a Tag File
Then, in the taglib directive of weekday_t.jsp, you can replace tagdir="/WEB-INF/tags" with uri="tagFiles" .
As you will see later in this chapter, you can also replace the somewhat cumbersome expression element with the more compact EL expression ${dayw}.
Bodied Tag
weekdayBody.tag
The standard action element jsp:doBody evaluates the body of the weekdayBody action and stores its output as a string into the page-scoped attribute dateAttr. The first line of the scriptlet then copies the attribute into the JSP variable named date. After that, the bodied tag file is identical to the bodyless one. This was not really necessary, but the subsequent code accesses the date twice, first to check that it isn’t empty and then to parse it. I didn’t like to invoke the getAttribute method twice. It seemed less tidy.
If you omit the attribute var, doBody sends the body’s result to the output; if you replace var with varReader, the result is stored as a java.io.Reader object instead of a java.lang.String; and if you add the attribute scope, you can specify var/varReader to be defined as a request, session, or application attribute, instead of in the page scope.
weekday_bt.jsp
You can import the project tagtest in Eclipse and test the example by typing in your browser http://localhost:8080/tldtest/weekday.jsp?d=2012-1225.
The tag Directive
Attributes of the tag Directive
Attribute | Description |
---|---|
description | The name says it all. |
display-name | A short name intended for tools. The default is the name of the tag file without the .tag extension. |
body-content | Same as the <body-content> tag in a TLD (see the comments after Listing 6-11). |
dynamic-attributes | If the attribute is present, its value identifies a scoped attribute where you store a map with names and values of the dynamic attributes you use when executing the tag. |
example | A string with a brief description of an example. |
small-icon | Path, relative from the tag file, of a small icon intended for tools. |
large-icon | Yes, you guessed correctly! |
language | Equivalent to its namesake of the page directive. |
pageEncoding | Ditto. |
isELIgnored | Ditto. |
The attribute Directive
Attributes of the attribute Directive
Attribute | Description |
---|---|
description | This attribute is almost universal. |
rtexprvalue | Same as the equally-named tag in a TLD (see the comments after Listing 6-11). |
type | Ditto. |
fragment | If set to true, it means that the attribute is a JSP fragment to be evaluated by the tag file. If false (the default), the attribute is a normal one and is therefore evaluated by Tomcat before being passed to the tag file. Do not specify rtexprvalue or type when you set fragment to true. Tomcat will set them for you, respectively, to true and javax.servlet.jsp.tagext.JspFragment. |
example | A string with a brief description of an example. |
small-icon | Path, relative from the tag file, of a small icon intended for tools. |
There are also two mutually exclusive pairs of attributes that are associated with JavaServer Faces: deferredValue/deferredValueType and deferredMethod/deferredMethodSignature. Let’s not put the cart before the oxen.
The variable Directive
Attributes of the variable Directive
Attribute | Description |
---|---|
description | No surprises here. |
name-given / name-from-attribute | You have seen name-given in the example. One of these two attributes must be present. The value of name-given cannot be the same of the value of the name attribute of an attribute directive or the value of a dynamic-attributes attribute of a tag directive. See after the end of this table for an explanation of how to use name-from-attribute. |
alias | It works together with name-from-attribute. Again, see the following details. |
scope | Can be AT_BEGIN, AT_END, or NESTED (the default). Once more, too much text to keep it in this table. See the following details. |
variable-class | The name of the class of the variable (default is java.lang.String). |
declare | Set to false (the default is true) if the variable is not declared. |
they can both invoke the tag file that includes the variable directive shown earlier. The tag file will then have an ali attribute containing "Good morning!" in the first case and "Stranger in a Strange Land" in the second case.
The attribute scope tells when Tomcat creates or updates the attribute in the calling page with the value of the attribute that is local to the tag file (perhaps a name like synchronization would have been clearer than scope). With AT_BEGIN, Tomcat does it before the tag file invokes a segment or immediately before exiting the tag file; with NESTED, only before invoking a segment; and with AT_END, only before leaving the tag file. Additionally, with NESTED, Tomcat saves the value of the calling-page attribute upon entering the tag file and restores it upon leaving it. But this only if an attribute with the given name exists in the calling page before entering the tag file.
JSTL and EL
Many developers have implemented similar custom actions to remove or at least reduce the need for scripting elements. Eventually, a new effective standard known as JavaServer Pages Standard Tag Library (JSTL) was born.
However, JSTL is of little use without the Expression Language (EL), which lets you access and manipulate objects in a compact and efficient way and can be used within the body of actions. I will first introduce you to EL, so that you’ll be well prepared to understand the JSTL examples.
JSP Expression Language
EL was introduced in JSP 2.0 as an alternative to the scripting elements. You can use EL expressions in template text and also in action attributes specified to be capable of accepting runtime expressions.
EL Expressions
EL supports two representations: ${expr} and #{expr}. To explain when you can or should use them, I must first clarify the distinction between lvalues and rvalues.
means that the result of the evaluation of j*3 (an rvalue) is to be assigned to the value resulting from the evaluation of ka[k] (an lvalue). Clearly, an lvalue must be a reference to something you can assign values to (a variable or some attribute of an object), while there is no such restriction on rvalues.
Suppose that you have a page with a form. Wouldn’t it be nice if you could specify directly in the input elements of the form the references to where the user’s inputs should be stored? For example, it’d be nice to specify something like <input id="firstName" value="variableName">, with variableName specifying where you want to store the input typed by the user. Then, when the form is submitted, there should be a mechanism to automatically take the user’s input and store it where you specified. Perhaps you could also define a new attribute of the input element to provide a validating method. Inside the input element, you would then already have everything you need to accept the user’s input, validate it, and store it away.
This sounds great, but if you set the value attribute of the input element to ${formBean.firstName}, this evaluates to an rvalue. The value of the firstName attribute of formBean is assigned to the value attribute of the input element, and that’s it. You need a way of deferring evaluation of formBean.firstName and use it as an lvalue when you really need it, that is, when you handle the form that was submitted.
You achieve that by replacing the $ before the EL braces with a #. The # tells Tomcat to defer evaluation and use its result as an lvalue or an rvalue, depending on the context. EL expressions with the dollar sign are evaluated like everything else. In any other aspect, parsing and evaluation of the two representations are identical. You will use the # representation when we will talk about JSF. For now, you can learn about EL using the $ representation.
Using EL Expressions
EL Expressions
EL Expression | Result |
---|---|
${1 <= (1/2)} | false |
${5.0 > 3} | true |
${100.0 == 100} | true |
${'a' < 'b'} | true |
${'fluke' gt 'flute'} | false |
${1.5E2 + 1.5} | 151.5 |
${1 div 2} | 0.5 |
${12 mod 5} | 2 |
${empty param.a} | true if the request parameter a is null or an empty string |
${sessionScope.cart.nItems} | The value of the nItems property of the session-scoped attribute named cart |
${aBean.aProp} | The value of the aProp property of the aBean bean |
${aMap[entryName]} | The value of the entry named entryName in the map named aMap |
The operators behave in general like in Java, but with one important difference: the equality operator (==) applied to string variables compares their contents, not whether the variables refer to the same instance of a string. That is, it behaves like Java’s String.equals() method.
In addition to EL operators identical to Java operators, you also have most of their literal equivalents: not for !, div for /, mod for %, lt for <, gt for >, le for <=, ge for >=, eq for ==, ne for !=, and for &&, and or for ||. You also have the unary operator empty, to be used as shown in one of the examples in Table 6-2.
The EL operators '.' (i.e., the dot) and [] (i.e., indexing) are more powerful and forgiving than the corresponding Java operators.
Furthermore, ${first.second.third}, equivalent to <%=first.getSecond().getThird()%>, returns null when first.second evaluates to null, although in the expression, we try to dereference it with .third. The JSP scripting equivalent would throw a NullPointerException. For this to work, all classes must implement the getter methods of properly formed Java beans.
Array indexing allows you to try to access an element that doesn’t exist, in which case it simply evaluates to null. For example, if you have an array of ten elements, the EL expression ${myArray[999]} returns null instead of throwing an ArrayIndexOutOfBoundsException, as Java would have done. It is not as bad as in the plain old “C” language, in which an index out of bounds would have returned the value it found in memory. With EL, you can check for null. And in general, you should do so, because you cannot rely on an exception being thrown, as it would be in Java.
There is a tiny difference, though: you cannot use the dot operator if the name of the key contains a character that confuses EL. For example, ${header["user-agent"]} is OK, but ${header.user-agent} doesn’t work, because the dash between user and agent in the second expression is interpreted as a minus sign. Unless you have a variable named agent, both header.user and agent evaluate to null and, according to the EL specification document, ${null - null} evaluates to zero. Therefore, the second expression would return a zero. You would encounter a different, but potentially more serious, problem if you had a map key containing a dot. For example, you could use ${param["my.par"]} without problems, but ${param.my.par} would probably result in a null or, almost certainly, in something other than what you are looking for. This would be bad in any case, because null is a possible valid outcome. I suggest you use the bracketed form in all occasions and simply forget this issue.
EL’s Implicit Objects
Object | Description |
---|---|
pageContext | The context of the JSP page. In particular, pageContext.servletContext gives you a reference to the same object referenced in JSP by the implicit variable application. Similarly, pageContext.session is equivalent to JSP’s session, pageContext.request to JSP’s request, and pageContext.response to JSP’s response. |
Param | Maps a request parameter name to its first value. |
paramValues | Maps a request parameter name to an array of its values. |
header | Maps a request header name to its first value. |
headerValues | Maps a request header name to an array of its values. |
Cookie | Maps a cookie name to a single cookie. |
initParam | Maps the name of a context initialization parameter to its value. |
pageScope | Maps page-scoped variable names to their values. |
requestScope | Maps request-scoped variable names to their values. |
sessionScope | Maps session-scoped variable names to their values. |
${aBean.aProp} | The value of the aProp property of the aBean bean. |
applicationScope | Maps application-scoped variable names to their values. |
You cannot use JSP scripting variables within EL expressions.
However, you have to pay attention to scope precedence. The variable set with c:set and the attribute in pageContext are the same variable. That is, c:set defines an attribute in the page context. The attribute in sessionContext is a different variable, and you cannot access it with ${xyz} because it is “hidden” behind the attribute with the same name in the page context. To access a session attribute, you have to prefix its name with sessionScope, as in ${sessionScope.xyz}. If you don’t specify a scope, EL looks first in the page, then in the request, then in the session, and finally in the application scope.
You cannot nest EL expressions. Expressions such as ${expr1[${expr2}]} are illegal.
However, you cannot mix the ${} and #{} forms.
JSP Standard Tag Library
JSTL Tag Libraries
Area | Functionality |
---|---|
core | Variable support, flow control, URL management, and miscellaneous |
i18n | Locale, message formatting, and number and date formatting |
functions | String manipulation and length of collection objects |
SQL | Handling of databases |
XML | XML core, flow control, and transformation |
The JSTL Tags
Core | i18n | Functions | Database | XML |
---|---|---|---|---|
c:catch | fmt:bundle | fn:contains | sql:dateParam | x:choose |
c:choose | fmt:formatDate | fn:containsIgnoreCase | sql:param | x:forEach |
c:forEach | fmt:formatNumber | fn:endsWith | sql:query | x:if |
c:forTokens | fmt:message | fn:escapeXml | sql:setDataSource | x:otherwise |
c:if | fmt:param | fn:indexOf | sql:transaction | x:out |
c:import | fmt:parseDate | fn:join | sql:update | x:param |
c:otherwise | fmt:parseNumber | fn:length | x:parse | |
c:out | fmt:requestEncoding | fn:replace | x:set | |
c:param | fmt:setBundle | fn:split | x:transform | |
c:redirect | fmt:setLocale | fn:startsWith | x:when | |
c:remove | fmt:setTimeZone | fn:substring | ||
c:set | fmt:timeZone | fn:substringAfter | ||
c:url | fn:substringBefore | |||
c:when | fn:toLowerCase | |||
fn:toUpperCase | ||||
fn:trim |
I will describe JSTL-XML and JSTL-SQL in the next chapters, while in the following sections of this chapter, I will describe JSTL-core and JSTL-i18n. I will not talk about the functions because they are pretty self-explanatory.
The Core Library
To explain some of the most used actions, I will go back to the example req_params.jsp of Chapter 1 (Listing 1-10) and add new scriptlets with JSTL actions in the same Eclipse project “test”. You can copy the code in the folder named jstl that you will find in the software package for this chapter. Listing 6-20 shows you how you do it.
c:out, c:set, and c:forEach (and fn:length)
req_params_jstl.jsp
Notice that, as there are no scripting elements, I have removed the importing of Java libraries.
Lines 02 and 03 show the taglib directives for JSTL core and functions. In line 05, you can see how to use the fn:length function to determine the size of the EL implicit object paramValues and the c:out action to send the value of an EL expression to the output. You could have just written the naked EL expression, but c:out automatically converts characters that have special HTML meaning to the corresponding HTTP entities. For example, it writes & instead of &. Therefore, it’s better to use c:out.
c:set initializes an index in line 08 and increments it in line 15. In lines 09 and 12, c:forEach lets you go through the elements of maps and arrays.
In essence, c:out is for EL expressions what an expression scripting element is for JSP (i.e., Java) expressions. Besides the attribute value, it supports default, to provide a fallback output, and escapeXml that you set to false to prevent the escaping of XML characters, which are escaped by default.
With c:set, besides defining var and value, you can also specify the scope of the variable with the attribute scope. Finally, in alternative to var, you can use the pair of attributes property and target to specify which property of which object you want to modify.
In the example, you have seen that c:forEach lets you loop over a list of objects with the two attributes var and items. Alternatively, you can go through a list by means of the attributes begin, where 0 indicates the first element, end, and step. Further, if you define the name of a variable by setting the attribute varStatus, c:forEach will store in it an object of type javax.servlet.jsp.jstl.core.LoopTagStatus.
As these attributes are pretty straightforward, the best way for you to become familiar with them is to write a small page and see what happens when you set them to different values.
c:forEach Types
Items | var |
---|---|
Array of instances of class C | Instance of C |
Array of primitive values (e.g., of int) | Wrapped element (e.g., in Integer) |
String of comma-delimited substrings | Substring |
java.util.Collection | Element obtained by invoking iterator() |
java.util.Map | Instance of java.util.Map.Entry |
java.util.Iterator | An Iterator element |
java.util.Enumeration | An Enumeration element |
javax.servlet.jsp.jstl.sql.Result | SQL rows |
c:if, c:choose, c:when, and c:otherwise
Besides test, which lets you define the condition you want to test, c:if supports the two attributes var and scope, which c:if uses to store the condition’s result.
There is no attribute supported by c:choose and c:otherwise, and c:when only supports test.
c:catch, c:remove, and c:url
c:remove lets you remove the variable defined in its pair of attributes var and scope.
Notice the nested double quotes. This is not a problem, because Tomcat processes the action on the server and replaces it with a string representing the URL. The client doesn’t see the inner double quotes.
You can also specify a var/scope pair of attributes to store the generated URL into a scoped variable and the attribute context to refer to another application. If you use the bodied form of c:url, you can define with c:param additional parameters that will be appended to the URL.
c:import, c:redirect, and c:param
c:import and c:redirect are generalized versions of the standard actions jsp:include and jsp:forward. The main difference is that the JSTL actions are not limited to the scope of the current application. They let you include or forward to any URL via the attribute url, which is in both cases the only attribute required.
By now, everything should be pretty clear to you. The only thing worth mentioning is that the default value for charEncoding is "ISO-8859-1". I prefer to use UTF-8 because it is equally supported by all operating systems. Also, UTF-8 can handle non-European languages and has become the de facto standard on the Web. In case you are curious, UTF stands for UCS Transformation Format, where UCS means Universal Character Set.
c:redirect is equivalent to invoking javax.servlet.http.HttpServletResponse.sendRedirect and only admits the two attributes url and context. When designing a website, you might find it useful to remember that c:redirect changes the page that the user sees, thereby affecting the setting of bookmarks, while with jsp:forward, the user remains unaware of the page change.
c:forTokens
In addition to c:forEach, the JSTL provides a form of string tokenizer, which lets you easily extract from a string the substrings separated by one or more delimiters.
If you have a comma as a single delimiter, you can use c:forEach, but if you have more than one delimiter, or if the only delimiter is not a comma, c:forTokens is for you.
The i18n Library: Writing Multilingual Applications
You can take one of two approaches to internationalizing a web application: you can either provide a different version of the JSP pages for each locale and select them via a servlet when processing each request, or you can save locale-specific data in separate resource bundles and access them via i18n actions . The JSTL internationalization actions support both, but I will concentrate on the second approach, where the work of switching between languages is actually done in JSP.
fmt:setLocale, fmt:setBundle, fmt:setMessage, and fmt:param
MyBundle_en.java
MyBundle_it.java
As you can see, a bundle is nothing other than a Java class that extends the class java.util.ListResourceBundle. In this example, you will only find a simple login page, but in reality, you’ll have to include all the language-specific messages of your application. I used the prefix login to show you that it’s possible to group messages within a bundle.
index.jsp of a Multilingual Application
Lines 04–07 ensure that the page variable langExt is not null by setting it to en when the page is requested the first time. Line 08 sets the locale to the requested language code. The list of valid language codes is defined in the International Organization for Standardization (ISO) 639 standard. They’re in lowercase (e.g., it for Italian), so you can’t confuse them with the country codes defined in the ISO 3166 standard, which are in uppercase (e.g., IT for Italy).
In line 09, you set the bundle. Notice that it looks like the fully qualified class name of the two bundle classes but without the trailing underscore and language code. This is exactly how it should be done. Otherwise, the JSTL won’t find your messages. After executing fmt:setBundle, the session variable lang points to the bundle in the correct language, thanks to the locale and the basename attribute.
Notice how the double quotes are nested in line 17 without causing any problem. This is because the actions are processed first. By the time Tomcat arrives to process the HTML, only the outer double quotes remain.
Besides value, fmt:setLocale admits two additional attributes. The first one, scope, defines the scope of the locale. In the example, the default (i.e., page) is used, but scope lets you, for example, save the locale as a session attribute. The remaining attribute, variant, lets you specify nonstandardized locales.
fmt:setMessage also supports a var/scope pair of attributes to let you store the generated string into a scoped variable. You can also place the sub-action fmt:param inside the body of fmt:message and set its attribute value to a string that you want to append to the message.
fmt:bundle, fmt:setTimeZone, and fmt:timeZone
but it can also store a time zone into a scoped variable specified by the var/scope attribute pair.
When you define a time zone with fmt:timeZone, on the other hand, it only applies to the elements that appear in the body of the action.
fmt:parseNumber and fmt: formatNumber
fmt:parseNumber and fmt:formatNumber deal with numbers, percentages, and currencies. Both actions can store their result into a scoped variable through the usual var/scope pair of attributes or send it to the output if those attributes are missing.
Note that fmt:formatNumber is bodyless and expects to find the number to be formatted in the value attribute. fmt:parseNumber also supports value in its bodyless form, but if the attribute is missing, it takes as input the content of its body.
Both actions support a type attribute that can have the values "number", "currency", or "percent".
Pattern Symbols for the Number Actions
Symbol | Meaning |
---|---|
0 | A digit |
E | Exponential form |
# | A digit; 0 when absent |
. | Decimal period |
, | Group of digits separator |
; | Format separator |
- | Default negative prefix |
% | Displays a percent sign after multiplying the number by 100 |
? | Displays a per mille sign after multiplying the number by 1000 |
¤ | Place marker for the actual currency sign |
X | Place marker for any other character used in the prefix or suffix |
' | To quote special characters in the prefix or suffix |
Try out fmt:formatNumber with different patterns and see what you get. Some symbols are obvious, others, less so.
Additionally, fmt:parseNumber supports the attributes parseLocale, integerOnly (that you must set to false when parsing a floating-point number), and timeZone.
Yet, on the other hand, fmt:formatNumber supports the attributes currencyCode (only when type is set to "currency"), currencySymbol (ditto), groupingUsed (set to false if you don’t want a separator between triplets of integer digits), maxIntegerDigits, minIntegerDigits, maxFractionDigits, and minFractionDigits. The last four attributes specify the maximum and minimum number of digits you want before and after the decimal point.
fmt:ParseDate, fmt:formatDate, and fmt:requestEncoding
Pattern Symbols for the Date Actions
Symbol | Meaning |
---|---|
G | Era designator (e.g., AD) |
y | Year |
M | Month |
d | Day of the month |
h | Hour (12-hour time) |
H | Hour (24-hour time) |
m | Minute |
s | Second |
S | Millisecond |
E | Day of the week |
D | Day of the year |
F | Day of the week within the month (e.g., 2 means second Tue of the month) |
w | Week of the year |
W | Week of the month |
a | AM/PM |
k | Hour (12-hour time) |
K | Hour (24-hour time) |
z | Time zone |
' | Escape for text |
'' | Quote |
Additionally, fmt:parseDate supports the attribute parseLocale, and fmt:formatDate supports type (with possible values "date", "time", and "both").
This action makes sense because the locale of the user might be different from the locale of the page. Note that if you develop a custom action that uses the method ServletResponse.setLocale() to set the locale of the response, it will take precedence over the character encoding set in fmt:requestEncoding.
Summary
In this chapter, you have learned everything about JSP standard actions and how to develop custom actions with JSP’s tag extension mechanism.
You saw detailed examples that explained how to develop and use custom actions with and without body, both implemented with tag handlers and with tag files.
After explaining the Expression Language, I described in general terms the JSP Standard Tag Library and explained in detail the core and the internationalization tags.
In the next chapter, I will introduce you to XML, an understanding of which is essential for developing professional web applications.