C H A P T E R  4

Image

JSP in Action

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.

JSP actions specify activities to be performed when a page is requested and can therefore operate on objects and affect the response. They normally take the following form:

<jsp:action-name attr1="value1" [attr2="value2"...]> ... </jsp:action-name>

However, actions can also have a body, like in the following example:

<jsp:action-name attribute-list>
  <jsp:subaction-name subaction-attribute-list/>
  </jsp:action-name>

There are eight JSP standard actions (forward, include, useBean, setProperty, getProperty, text, element, and plugin) and five additional actions that can only appear in the body of other actions (param, params, attribute, body, and fallback).

Actually, to be precise, there are two additional action elements—invoke and doBody—that you cannot invoke from within JSP pages. More about them later in this chapter. 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 forward action lets you abort execution of the current page and transfer the request to another page:

<jsp:forward page="myOtherPage.jsp">
  <jsp:param name="newParName" value="newParValue"/>
  </jsp:forward>

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.

Here is another example of a forward action:

<% String dest = "/myJspPages/" + someVar; %>
<jsp:forward page="<%=dest%>">
  <jsp:param name="newParName" value="newParValue"/>
  </jsp:forward>

This is 100 percent equivalent to the following scriptlet:

<%
  String dest = "/myJspPages/" + someVar;
  RequestDispatcher rd = application.getRequestDispatcher(dest + "?newParName=newParValue");
  rd.forward(request, response);
  %>

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.

With both forward and include, the destination page must be a well-formed and complete JSP page. The forward action must satisfy the additional requirement of generating a complete and valid HTML page, because the output of the destination page is what goes back to the client’s browser in the HTML response. The destination page of an include action might even generate only a single character, although in most cases it provides HTML code. For example, the top bar of the eshop application is generated in the page TopMenu.jsp (see Listing 4-1) and included in seven JSP pages with this code:

<jsp:include page="TopMenu.jsp" flush="true"/>

The flush attribute (default false) ensures that the HTML generated so far by the including page is sent to the client before executing the included page. Note that the included page is not allowed to change the response headers or the status code.

Listing 4-1. TopMenu.jsp

<%@page language="java" contentType="text/html"%>
<%
  String base = (String)application.getAttribute("base");
  String imageUrl = (String)application.getAttribute("imageUrl");
  %>
<div class="header">
  <div class="logo">
    <p>e-Shopping Center</p>
    </div>
  <div class="cart">
    <a class="link2" href="<%=base%>?action=showCart">Show Cart
      <img src="<%=imageUrl%>/cart.gif" border="0"/></a>
    </div>
  </div>

TopMenu.jsp generates the HTML code in Listing 4-2 (shown after I removed the empty lines).

Listing 4-2. HTML Generated by TopMenu.jsp

<div class="header">
  <div class="logo">
    <p>e-Shopping Center</p>
    </div>
  <div class="cart">
    <a class="link2" href="/eshop/shop?action=showCart">Show Cart
      <img src="/eshop/images//cart.gif" border="0"/></a>
    </div>
  </div>

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 4-3). To try it out, copy to the usual test folder (webappsROOT ests) the folder named jsp_includes that you will find in the software package for this chapter, and then type localhost:8080/tests/jsp_includes/includes.jsp in a web browser.

Listing 4-3. includes.jsp

<%@page language="java" contentType="text/html"%>
<html><head><title>A</title></head><body>
<table border="1">
  <tr><th>incl B</th><th>incl C</th><th>C contains</th></tr>
  <tr><td>jsp:include</td><td>jsp:include</td><td><jsp:include page="d/b_act.jsp"/></td></tr>
  <tr><td>jsp:include</td><td>@include</td><td><jsp:include page="d/b_dir.jsp"/></td></tr>
  <tr><td>@include</td><td>jsp:include</td><td><%@include file="d/b_act.jsp"%></td></tr>
  <tr><td>@include</td><td>@include</td><td><%@include file="d/b_dir.jsp"%></td></tr>
  </table>
</body></html>

As you can see, I first included the d/b_act.jsp and d/b_dir.jsp files with an include action and then with an include directive. The two files contain these lines, respectively:

<%@page language="java" contentType="text/html"%><jsp:include page="c.txt"/>
<%@page language="java" contentType="text/html"%><%@include file="c.txt"%>

I placed a c.txt file (only containing the letter A) in the directory of includes.jsp and a second c.txt file (only containing the letter B) in the d directory. Figure 4-1 shows the result of running includes.jsp.

Image

Figure 4-1. The output of 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 useBean action declares a new JSP scripting variable and associates a Java object to it. For example, the following code declares the variable dataManager of type eshop.model.DataManager:

<jsp:useBean id="dataManager" scope="application" class="eshop.model.DataManager"/>

This is the same data manager instantiated and configured in ShopServlet.java as you saw in Chapter 3 (Listing 3-9). JSP uses this variable to access the data without having to worry about its location and implementation. Within eshop, this is the only way for JSP (the View) to interact with the data manager (the Model). For example, when a user selects a book and clicks on the link to add it to the shopping cart, the controller servlet executes ShoppingCart.jsp with an argument set to the book identifier. Then, ShoppingCart.jsp executes a method of the data manager (see Table 3-1) to obtain the book details, which are actually stored in a MySQL database:

Book book = dataManager.getBookDetails(bookId);

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.

Image Caution The use of jsp:useBean is not for the faint hearted!

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_test.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, if you specify type and set it to a fully qualified class name but specify neither class nor beanName, Tomcat won’t instantiate any object and will instead look for it in the given scope. If it finds it, Tomcat will make it available as an object of the given type rather than of the class from which it was instantiated. If what I just explained sounds confusing, you might decide to follow a simple rule: forget that jsp:useBean supports the attributes class and beanName. Let the servlet do the work. Just 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 3.9) first instantiated the dataManager object and then saved it in the application scope.

Image Caution 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

As an example of useBean scopes, the following code instantiates a MyClass object that remains available as long as the session remains valid:

<jsp:useBean class="myPkg.MyClass" id="myObj" scope="session"/>

You’ll be able to access it via a scripting variable named myObj in any page within the same session with the following statement:

<jsp:useBean id="myObj" type="myPkg.MyClass" scope="session"/>

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.

Incidentally, the first useBean (with class, id, and scope) is completely equivalent to this:

<%
MyClass myName = new MyClass();
session.setAttribute("myObj", myObj);
%>

and the second useBean (with id, type, and scope) is the same as this:

<%
MyClass myObj = (MyClass)session.getAttribute("myObj");
%>

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 put methods . To make it completely clear, both get and put must be there. Otherwise, that class attribute is not a bean property.

Additionally, you must name the two methods respectively get and put, 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.

An example from the eshop application will convince you that you are better off if Tomcat recognizes an attribute as a property. The JSP page OrderConfirmation.jsp has the following two elements:

<jsp:useBean id="customer" class="eshop.beans.Customer"/>
<jsp:setProperty property="*" name="customer"/>

The useBean action instantiates an object of type Customer and assigns it to the variable named customer. The action is equivalent to:

Customer customer = new Customer();

By defining property="*", the setProperty action tells Tomcat to set all bean properties of the newly created object. What setProperty does not say is to what values they should be set. This is because the values come from request parameters named exactly like the properties. Check out the definition of the Customer class, shown in Listing 4-4.

Listing 4-4. Customer.java

package eshop.beans;

public class Customer {
  private String contactName = "";
  private String deliveryAddress = "";
  private String ccName = "";
  private String ccNumber = "";
  private String ccExpiryDate = "";

  public String getContactName() {
    return contactName;
    }
  public void setContactName(String contactName) {
    this.contactName = contactName;
    }

  public String getDeliveryAddress() {
    return deliveryAddress;
    }
  public void setDeliveryAddress(String deliveryAddress) {
    this.deliveryAddress = deliveryAddress;
    }

  public String getCcName() {
    return ccName;
    }
  public void setCcName(String ccName) {
    this.ccName = ccName;
    }

  public String getCcNumber() {
    return ccNumber;
    }
  public void setCcNumber(String ccNumber) {
    this.ccNumber = ccNumber;
    }

  public String getCcExpiryDate() {
    return ccExpiryDate;
    }
  public void setCcExpiryDate(String ccExpiryDate) {
    this.ccExpiryDate = ccExpiryDate;
    }
  }

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.

Image Caution The use of property="*" can have confusing outcomes if parameters and attributes are not correctly matched

So far so good. Not so interesting. But what is interesting is that the setProperty action

<jsp:setProperty property="*" name="customer"/>

is equivalent to the following:

customer.setContactName(request.getParameter("contactName");
customer.setDeliveryAddress(request.getParameter("deliveryAddress");
customer.setCcName(request.getParameter("ccName");
customer.setCcNumber(request.getParameter("ccNumber"));
customer.setCcExpiryDate(request.getParameter("ccExpiryDate"));

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.

Also jsp:getProperty is useful, because it sends the value of a property to the output. For example, suppose you define MyClass as shown in Listing 4-5.

Listing 4-5. MyClass.java

package MyClasses;
import java.io.Serializable;
public class MyClass implements java.io.Serializable {
  public static final long serialVersionUID = 1L;
  private int i;
  public MyClass() {i = 0;}
  public void setI(int i) {this.i = i;}
  public int getI() {return i;}
  }

As you can see, the integer attribute i is a property. Listing 4-6 shows a JSP page that uses both getProperty and setProperty.

Listing 4-6. myObj.jsp

<%@page language="java" contentType="text/html"%>
<%@page import="java.util.*, MyClasses.MyClass"%>
<%@page trimDirectiveWhitespaces="true"%>
<html><head><title>myObj</title></head><body>
<jsp:useBean id="obj" class="MyClasses.MyClass" scope="session">
  <jsp:setProperty name="obj" property="i" value="11"/>
  </jsp:useBean>
<jsp:getProperty name="obj" property="i"/>
<jsp:setProperty name="obj" property="i" value="22"/>
<jsp:getProperty name="obj" property="i"/>
</body></html>

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 two executions of getProperty send the value of i to the output. As a result, myObj.jsp generates the following HTML page:

<html><head><title>myObj</title></head><body>
1122</body></html>

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 12 being “fused” into 1122. Not necessarily what you would like to have.

Action: text

You can use the jsp:text action to write template text. Its syntax is straightforward:

<jsp:text>Template data</jsp:text>

Its body cannot contain other elements; it can only contain text and EL expressions.

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.

The JSP page shown in Listing 4-7 generates the HTML output shown in Listing 4-8. It is a meaningless page, only designed to show you how to use these actions. Don’t look for a meaning that doesn’t exist!

Listing 4-7. actel_element_attribute.jsp

<%@page language="java" contentType="text/html"%>
<html>
<head><title>Action elements: element, attribute</title></head>
<body>
<jsp:element name="myElem">
  <jsp:attribute name="myElemAttr">myElemAttr's value</jsp:attribute>
  <jsp:body>myElem's body</jsp:body>
  </jsp:element>
<br/>
<jsp:include page="text.txt"/>
<br/>
<jsp:include>
  <jsp:attribute name="page">text.txt</jsp:attribute>
  </jsp:include>
</body>
</html>

Listing 4-8. The Output of actel_element_attribute.jsp

<html>
<head><title>Action elements: element, attribute</title></head>
<body>
<myElem myElemAttr="myElemAttr's value">myElem's body</myElem>
<br/>
This is inside the test file text.txt
<br/>
This is inside the test file text.txt
</body>
</html>

I have highlighted two parts of the listings. The first highlight shows how to use the actions element, attribute, and body to generate an XML element. Be aware that if you drop the action body, the XML element generated by element will have an empty body, as in the following example:

<myElem myElemAttr="myElemAttr's value"/>

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.

Actions: plugin, params, and fallback

These three actions let you embed an object in a web page. For example, Listing 4-9 shows you how to embed an applet with plugin, how to pass to it a line of text with params, and how to inform the user with fallback if the applet fails to start. To test it, copy the file plugin.jsp from the jsp_plugin folder of the software package for this chapter to the usual tests folder, and then browse localhost:8080/tests/plugin.jsp.

The plugin action generates for you the appropriate browser-dependent HTML construct to embed the applet.

Listing 4-9. plugin.jsp

<%@page language="java" contentType="text/html"%>
<html><head><title>Action: plugin</title></head><body>
<jsp:plugin type="applet" code="MyApplet.class"
  codebase="/tests" height="100" width="100">
  <jsp:params>
    <jsp:param name="line" value="Well said!"/>
    </jsp:params>
  <jsp:fallback>Unable to start plugin</jsp:fallback>
  </jsp:plugin>
</body></html>

If you want to try it yourself, Listing 4-10 shows you the code for a simple applet.

Listing 4-10. MyApplet.java

import java.awt.*;
import java.applet.*;
public class MyApplet extends Applet {
  String line;
  public void init() {
    line = getParameter("line");
    }
  public void paint(Graphics page) {
    page.setColor(Color.red);
    page.fillRect(0, 0, 50, 50);
    page.setColor(Color.green);
    page.fillRect(50, 0, 50, 50);
    page.setColor(Color.blue);
    page.fillRect(0, 50, 50, 50);
    page.setColor(Color.yellow);
    page.fillRect(50, 50, 50, 50);
    page.setColor(Color.black);
    page.drawString(line, 10, 40);
    }
  }

And Figure 4-2 shows what you should see in your browser. To test it, compile the applet, place both plugin.jsp and MyApplet.class in the folder %CATALINA_HOME%webappsROOT ests, and type localhost:8080/tests/plugin.jsp in your browser. Note that Tomcat doesn’t do anything with the applet itself. It only sends it to the client when requested to do so. That’s why the applet class doesn’t need to be placed in the WEB-INF folder like the other classes you have encountered so far.

Image

Figure 4-2. The output of plugin.jsp

jsp:plugin accepts the attributes type, jreversion, nspluginurl, and iepluginurl. The example used type to specify that the plugin was an applet; jreversion lets you specify the version number of the JRE specification you require (the default is 1.2); and nspluginurl and iepluginurl let you specify where the JRE plug-in can be downloaded for Netscape Navigator and Internet Explorer, respectively. However, I doubt that you will ever use the last three attributes.

Comments and Escape Characters

The comment delimiters <%-- .. --%> have in JSP the same function as /* .. */ in Java. You can also use them to “switch off” JSP elements, as shown here:

<%-- <jsp:include page="whatever.jsp"/> --%>

They can also span over several lines.

Image Note 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 &lt;% and %&gt;.

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.

To write in a JSP page a statement like

<myPrefix:myActionTag attributeName="myAttributeName"/>

you need to follow the following steps:

  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

A bodyless action is an element that, not having an end tag, cannot enclose a body between start and end tags. As an example, let’s say you want to develop an action that prints the day of the week of any given date:

<wow:weekday date="date"/>

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, copy the folder tags to Tomcat’s webapps folder.

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.

To satisfy Serializable, you only need to define a unique identifier, like this:

static final long serialVersionUID = 1L;

The value identifies the version of your class and the objects you instantiate from it. It is then used when deserializing objects to check that class and object match. As long as you don’t have several versions of the class and swap objects between JVMs, you don’t really need to worry about it. However, to satisfy the Tag interface, you have to define the methods listed in Table 4-1.

Image

Image

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.

Listing 4-11 shows you the code of the whole tag handler.

Listing 4-11. WeekdayTag.java

package tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class WeekdayTag extends TagSupport {
  static final long serialVersionUID = 1L;
  static final String[] WD = {"","Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  private String date;

  public void setDate(String date) {
    this.date = date;
    }

  public int doEndTag() throws JspException {
    GregorianCalendar cal = new GregorianCalendar();
    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
    fmt.setLenient(true);
    if (date != null && date.length() > 0) {
      Date d = new Date();
      try {
        d = fmt.parse(date);
        }
      catch (Exception e) {
        throw new JspException("Date parsing failed: " + e.getMessage());
        }
      cal.setTime(d);
      }
    try {
      pageContext.getOut().print(WD[cal.get(Calendar.DAY_OF_WEEK)]);
      }
    catch (Exception e) {
      throw new JspException("Weekday writing failed: " + e.getMessage());
      }
    return EVAL_PAGE;
    }
  }

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.

In any case, you must place your handlers in WEB-INFclasses. For example, as WeekDayTag.java belongs to the package named tags, its compiled class must go into the folder WEB-INFclasses ags.

Step 2: Define the TLD

The TLD is an XML file that describes your tags so that Tomcat knows how to deal with them. Listing 4-11 shows the full TLD for the custom tag library.

Listing 4-11. wow.tld

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ~CCC
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
  <description>Example of a simple tag library</description>
  <tlib-version>1.0</tlib-version>
  <short-name>wow</short-name>
  <tag>
    <description>Displays the day of the week</description>
    <display-name>weekday</display-name>
    <name>weekday</name>
    <tag-class>tags.WeekdayTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
      <name>date</name>
      <type>java.lang.String</type>
      <rtexprvalue>true</rtexprvalue>
      </attribute>
    </tag>
  </taglib>

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 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.

Note that up to JSP 2.0, body content was mandatory, and body content="JSP" was valid. This is no longer the case with JSP 2.1.

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.

If you had used a type other than String, the value passed to the tag handler would have been of that type. For example, with an attribute defined like this:

<attribute>
  <name>num</name>
  <type>java.lang.Integer</type>
  </attribute>

you would have included the following code in the tag handler:

private int num;
public void setNum(Integer num) {
  this.num = num.intValue();
  }

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

Listing 4-12 shows you a simple JSP page to test the weekday custom action.

Listing 4-12. weekday.jsp

1: <%@page language="java" contentType="text/html"%>
2: <%@taglib uri="/WEB-INF/tlds/wow.tld" prefix="wow"%>
3: <% String d = request.getParameter("d"); %>
4: <html><head><title>weekday bodyless tag</title></head><body>
5: weekday today: <wow:weekday/><br/>
6: weekday <%=d%>: <wow:weekday date="<%=d%>"/>
7: </body></html>

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/tags/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. 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:

org.apache.jasper.JasperException: javax.servlet.ServletException: Image
  javax.servlet.jsp.JspException: Image
    Date parsing failed: Unparseable date: "2012-1225"

To try out the example, you can simply copy the folder named tags from the code of Chapter 4 to Tomcat’s webapps folder. You can then execute the example by typing in your browser localhost:8080/tags/weekday.jsp.

Note that if you modify the tag handler, you need to recompile it from the command line. To do so, type the following two commands:

cd c:program filesapache software foundation omcatwebapps agsweb-infclasses ags
javac -classpath "C:Program FilesApache Software FoundationTomcatlibjsp-api.jar" Image
  WeekDayTag.java

You then also have to restart Tomcat.

Bodied Custom Actions

To show you the differences from the bodyless action, I will implement a version of the weekday action that expects the date in its body instead of in an attribute:

<wow:weekdayBody>date</wow:weekdayBody>
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.

An additional complication concerns the default date: if you write the action with an empty body, as follows:

<wow:weekdayBody></wow:weekdayBody>

the method doAfterBody won’t be executed at all. How can you then print out the default day?

The answer is simple: you have to overwrite the doEndTag method and write the default date from there in case there is no body. Listing 4-13 shows the end result.

Listing 4-13. WeekdayBodyTag.java

package tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class WeekdayBodyTag extends BodyTagSupport {
  static final long serialVersionUID = 1L;
  static final String[] WD = {"","Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  private boolean bodyless = true;  /* 1 */

  public int doAfterBody() throws JspException {
    String date = getBodyContent().getString();  /* 2 */
    if (date.length() > 0) {
      GregorianCalendar cal = new GregorianCalendar();
      Date d = new Date();
      SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
      fmt.setLenient(true);
      try {
        d = fmt.parse(date);
        }
      catch (Exception e) {
        throw new JspException("Date parsing failed: " + e.getMessage());
        }
      cal.setTime(d);
      try {
          getPreviousOut().print(WD[cal.get(Calendar.DAY_OF_WEEK)]);  /* 3 */
          }
        catch (Exception e) {
          throw new JspException("Weekday writing failed: " + e.getMessage());
          }
      bodyless = false;  /* 4 */
      }
    return SKIP_BODY;
    }

  public int doEndTag() throws JspException {
    if (bodyless) {  /* 5 */
      GregorianCalendar cal = new GregorianCalendar();
      try {
        pageContext.getOut().print(WD[cal.get(Calendar.DAY_OF_WEEK)]);
        }
      catch (Exception e) {
        throw new JspException("Weekday writing failed: " + e.getMessage());
        }
      }
    return EVAL_PAGE;
    }
  }

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

To define the new action, you only need to add the <tag> shown in Listing 4-14 after the <tag> for the bodyless weekday action.

Listing 4-14. The tag Element for weekdayBody

  <tag>
    <description>Displays the day of the week</description>
    <display-name>weekdayBody</display-name>
    <name>weekdayBody</name>
    <tag-class>tags.WeekdayBodyTag</tag-class>
    <body-content>scriptless</body-content>
    </tag>

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

Listing 4-15 shows a modified version of weekday.jsp to handle the bodied tag.

Listing 4-15. weekday_b.jsp for the Bodied Action

<%@page language="java" contentType="text/html"%>
<%@taglib uri="/WEB-INF/tlds/wow.tld" prefix="wow"%>
<html><head><title>weekday bodied tag</title></head><body>
weekdayBody today: <wow:weekdayBody></wow:weekdayBody><br/>
weekdayBody ${param.d}: <wow:weekdayBody>${param.d}</wow:weekdayBody><br/>
</body></html>

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.

Image Tip Many tag libraries are available on the Internet. JSTL provides many actions that you can use and reuse, which I will write about in the next section. It certainly pays, in terms of both quality and efficiency, to avoid developing actions from scratch unless they give you clear and quantifiable benefits. You will find more info on JSTL at www.oracle.com/technetwork/java/index-jsp-135995.html.

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 tag files of the software package for this chapter. To install them, follow the instructions contained in README.txt.

Bodyless Tag

Listing 4-16 shows the tag-file version of the tag handler WeekdayTag.java that you saw in Listing 4-11.

Listing 4-16. weekday.tag

<%@tag import="java.util.Date, java.text.SimpleDateFormat"
       import="java.util.Calendar, java.util.GregorianCalendar"%>
<%@attribute name="date" required="false"%>
<%
  final String[] WD = {"","Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  GregorianCalendar cal = new GregorianCalendar();
  if (date != null && date.length() > 0) {
    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
    fmt.setLenient(true);
    Date d = fmt.parse(date);
    cal.setTime(d);
    }
  out.print(WD[cal.get(Calendar.DAY_OF_WEEK)]);
  %>

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().

Listing 4-17 shows how you modify weekday.jsp of Listing 4-12 to use the tag file.

Listing 4-17. weekday_t.jsp

<%@page language="java" contentType="text/html"%>
<%@taglib tagdir="/WEB-INF/tags" prefix="wow"%>
<% String d = request.getParameter("d"); %>
<html><head><title>weekday bodyless tag</title></head><body>
weekday today: <wow:weekday/><br/>
weekday <%=d%>: <wow:weekday date="<%=d%>"/><br/>
</body></html>

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".

To keep the uri attribute, you need to declare the tag file in a TLD, as shown in Listing 4-18.

Listing 4-18. wow.tld for a Tag File

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ~CCC
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
  <description>My library of tag files</description>
  <tlib-version>1.0</tlib-version>
  <short-name>wow</short-name>
  <uri>tagFiles</uri>
  <tag-file>
    <description>Displays the day of the week</description>
    <display-name>weekday</display-name>
    <name>weekday</name>
    <path>/WEB-INF/tags/weekday.tag</path>
    </tag-file>
  </taglib>

Then, in the taglib directive of weekday_t.jsp, you can replace tagdir="/WEB-INF/tags" with uri="tagFiles".

As an example of the variable directive, replace in weekday.tag the line

out.print(WD[cal.get(Calendar.DAY_OF_WEEK)]);

with the following two:

%><%@variable name-given="dayw" scope="AT_END"%><%
jspContext.setAttribute("dayw", WD[cal.get(Calendar.DAY_OF_WEEK)]);

The action will then save the string with the day of the week into the attribute dayw instead of sending it directly to the output. To display the action’s result from within weekday_t.jsp, insert the following expression element after executing the action:

<%=pageContext.getAttribute("dayw")%>

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

Listing 4-19 shows the tag file equivalent to the tag handler WeekdayBodyTag.java, which you saw in Listing 4-13. I wrote it by modifying the tag file weekday.tag that implemented the bodyless tag as shown in Listing 4-16. Listing 4-20 is the tag-file equivalent to weekday_b.jsp (see Listing 4-15), which invoked the bodied tag handler. I wrote it by modifying weekday_t.jsp of Listing 4-17, which used the bodyless tag file.

Listing 4-19. weekdayBody.tag

<%@tag import="java.util.Date, java.text.SimpleDateFormat"
       import="java.util.Calendar, java.util.GregorianCalendar"%>
<jsp:doBody var="dateAttr"/>
<%
  String date = (String)jspContext.getAttribute("dateAttr");
  final String[] WD = {"","Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  GregorianCalendar cal = new GregorianCalendar();
  if (date.length() > 0) {
    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
    fmt.setLenient(true);
    Date d = fmt.parse(date);
    cal.setTime(d);
    }
  out.print(WD[cal.get(Calendar.DAY_OF_WEEK)]);
  %>

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.

You should know that jsp:invoke is very similar to jsp:doBody but operates on a JSP fragment instead of the action body. For example, by writing the following two lines in a tag file

<%@attribute name="fragName" fragment="true"%>
<jsp:invoke fragment="fragName"/>

you pass to it a JSP fragment. Like doBody, invoke admits the attributes var, varReader, and scope. Both standard actions can only be used within tag files.

Listing 4-20. weekday_bt.jsp

<%@page language="java" contentType="text/html"%>
<%@taglib tagdir="/WEB-INF/tags" prefix="wow"%>
<html><head><title>weekday bodied tag</title></head><body>
weekdayBody today: <wow:weekdayBody></wow:weekdayBody><br/>
weekdayBody ${param.d}: <wow:weekdayBody>${param.d}</wow:weekdayBody><br/>
</body></html>
The tag Directive

In the previous section, you encountered the import attribute of the tag directive. Table 4-2 lists the other attributes that are available. They are all optional.

Image

The attribute Directive

You have already encountered the attributes name and required. Table 4-3 briefly describes the remaining ones (all optional).

Image

Image

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

Table 4-4 briefly describes all the attributes.

Image

While name-given provides the name of a JSP attribute (which, as you will see in the next section, coincides with the name of an EL variable), name-from-attribute provides the name of another JSP attribute containing the name of the JSP attribute you are interested in. Then, alias provides the name of an EL variable local to the tag file that Tomcat synchronizes with the JSP attribute. For example, if you declare:

<%@variable alias="ali" name-from-attribute="attrName"%>

Tomcat, before continuing execution of the tag file, makes available to it the page-scoped attribute named ali and sets it to the value of the attribute named attrName. This name redirection makes possible for JSP pages that use differently named attributes to use the same tag file. For example, a.jsp might include the line

session.setAttribute("greet", "Good morning!");

and b.jsp might have

application.setAttribute("novel", "Stranger in a Strange Land");

If a.jsp contains

pageContext.setAttribute("attrName", "greet");

and b.jsp

pageContext.setAttribute("attrName", "novel");

they can both invoke the tag file that includes the variable directive shown above. 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 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.

But first of all, you have to download two libraries from http://jstl.java.net/download.html. If you follow the two links "JSTL API" and "JSTL Implementation", you will reach two pages from which you can download respectively javax.servlet.jsp.jstl-api-1.2.1.jar and javax.servlet.jsp.jstl-1.2.1.jar (or the equivalent files for the version that will be current when you will be reading this book).

Copy them to %CATALINA_HOME%lib and restart Tomcat.

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. You will find a good reference at http://java.sun.com/products/jsp/syntax/2.0/syntaxref207.html#1010522.

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.

The l stands for left, and the r stands for right. These values refer to the fact that in most computer languages, the assigned value is on the right-hand side of an assignment statement, while the value to be assigned to it is on the left-hand side. For example, the Java statement

ka[k] = j*3;

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

The expr in ${expr} can contain literals, operators, and references to objects and methods. Table 4-5 shows some examples and their results.

Image

Image

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 4-2.

The EL operators '.' (i.e., the dot) and [] (i.e., indexing) are more powerful and forgiving than the corresponding Java operators.

When applied to a bean, as in ${myBean.prop}, the dot operator is interpreted as an indication that the value of the property should be returned, as if you’d written myBean.getProp() in a scripting element. As a result, for example, the line of code

${pageContext.servletContext.servletContextName}

is equivalent to this:

<%=pageContext.getServletContext().getServletContextName()%>

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.

You can use both the dot and indexing operator to access maps. For example, the following two EL expressions both return the value associated with the key named myKey:

${myMap.myKey}
${myMap["myKey"]}

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.

Similar to JSP, EL contains implicit objects, which you find listed in Table 4-6.

Image

Image Caution You cannot use JSP scripting variables within EL expressions.

You’ve probably noticed that EL doesn’t include any way of declaring variables. Within EL expressions, you can use variables set with the c:set JSTL core action (which I will describe in the next section) or scoped attributes. For example, all of the following definitions let you use the EL expression ${xyz}:

<c:set var="xyz" value="33"/>
<% session.setAttribute("xyz", "44"); %>
<% pageContext.setAttribute("xyz", "22"); %>

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.

Image Caution You cannot nest EL expressions. Expressions such as ${expr1[${expr2}]} are illegal.

You can make composite expressions consisting of several EL expressions and additional text, as in the following example:

<c:set var="varName" value="Welcome ${firstName} ${lastName}!"/>

However, you cannot mix the ${} and #{} forms.

JSP Standard Tag Library

JSTL consists of five tag libraries, as listed in Table 4-7. If you are wondering, i18n stands for internationalization, abbreviated by replacing the eighteen letters in the middle with the number 18.

Image

Image

Table 4-8 lists all the tags defined in the five libraries.

Image

As you have already seen in a couple of examples, to use the JSTL libraries in JSP pages, you must declare them in taglib directives as follows:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>
<%@taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml"%>

I will describe JSTL-XML in Chapter 5, where I will talk about XML, and JSTL-SQL in Chapter 6, dedicated to database access from JSP. 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-6) and replace its scriptlets with JSTL actions. Listing 4-16 shows you how you do it.

c:out, c:set, and c:forEach (and fn:length)

Listing 4-16. req_params_jstl.jsp

01: <%@page language="java" contentType="text/html"%>
02: <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
03: <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
04: <html><head><title>Request Parameters with JSTL</title></head><body>
05:   Map size = <c:out value="${fn:length(paramValues)}"/>
06:   <table border="1">
07:     <tr><td>Map element</td><td>Par name</td><td>Par value[s]</td></tr>
08:     <c:set var="k" value="0"/>
09:     <c:forEach var="par" items="${paramValues}"><tr>
10:       <td><c:out value="${k}"/></td>
11:       <td><c:out value="'${par.key}'"/></td>
12:       <td><c:forEach var="val" items="${par.value}">
13:         <c:out value="'${val}'"/>
14:         </c:forEach></td>
15:       <c:set var="k" value="${k+1}"/>
16:       </tr></c:forEach>
17:     </table>
18: </body></html>

Notice that, as there are no scripting elements, I have removed the importing of Java libraries.

Lines 2 and 3 show the taglib directives for JSTL core and functions. In Line 5, 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 &amp; instead of &. Therefore, it’s better to use c:out.

c:set initializes an index in line 8 and increments it in line 15. In lines 9 and 12, c:forEach lets you go through the elements of maps and arrays.

If you type in your browser

http://localhost:8080/tests/req_params_jstl.jsp?a=b&c=d&a=zzz&empty=&empty=&1=22

you’ll get the same output shown in Figure 1-12 for req_params.jsp (see Figure 4-3). Although, to be completely correct, the format of the HTML will be different.

Image

Figure 4-3. Output of req_params_jstl.jsp

In essence, c:out is for EL expressions what an expression scripting element is for JSP (i.e., Java) expressions. Beside 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.

Before moving on, have a look at Table 4-9. It lists what types of objects you can assign to the attribute items and, correspondingly, what type of objects you get in the variable defined through var.

Image

Image

c:if, c:choose, c:when, and c:otherwise

The JSTL versions of Java’s if and switch are particularly useful tags. For example, the body of the following action is executed only if the EL expression calculates to true:

<c:if test="EL-expression"> ... </c:if>

Unfortunately, there is no c:else, but the JSTL version of switch (c:choose) is much more powerful than its Java counterpart. In fact, it’s more like a chain of if .. else:

<c:choose>
  <c:when test="EL-expression-1"> ... </c:when>
  <c:when test="EL-expression-2"> ... </c:when>
  ...
  <c:otherwise> ... </c:otherwise>
  </c:choose>

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

With c:catch you can catch the exceptions that occur within its body. It accepts a var attribute, where it stores the result of the exception, of type java.lang.Throwable. For example, the following two lines will insert into the output the string "java.lang.ArithmaticException: / by zero".

<c:catch var="e"><% int k = 1/0; %></c:catch>
<c:if test="${e != null}"><c:out value="${e}"/></c:if>

c:remove lets you remove the variable defined in its pair of attributes var and scope.

c:url formats a string into a URL, which it then inserts into the output, like in the following example:

<a href="<c:url value="/tests/hello.jsp"/>">Hello World</a>

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.

The general syntax of c:import is as follows:

<c:import url="expr1" context="expr2" charEncoding="expr3" var="name" scope="scope">
  <c:param name="expr4" value="expr5"/>
  ...
  </c:import>

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 web site, 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 sub-strings 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 general syntax of c:forTokens is as follows:

<c:forTokens var="name" items="expr1" delims="expr2" varStatus="name"
    begin="expr3" end="expr4" step="expr5">
  ...
</c:forTokens>

The i18n Library: Writing Multi-Lingual 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

Suppose that you want to support English and Italian. The first thing you have to do is identify all the strings that are going to be different in the two languages and define two bundles, one for each language (see Listings 4-17 and 4-18).

Listing 4-17. MyBundle_en.java

package myPkg.i18n;
import java.util.*;
public class MyBundle_en extends ListResourceBundle {
  public Object[][] getContents() {return contents;}
  static final Object[][] contents = {
    {"login.loginmess","Please login with ID and password"},
    {"login.submit","Submit"},
    {"login.choose","Choose the language"},
    {"login.english","English"},
    {"login.italian","Italian"}
    };
  }

Listing 4-18. MyBundle_it.java

package myPkg.i18n;
import java.util.*;
public class MyBundle_it extends ListResourceBundle {
  public Object[][] getContents() {return contents;}
  static final Object[][] contents = {
    {"login.loginmess","Loggati con ID e parola d'ordine"},
    {"login.submit","Invia"},
    {"login.choose","Scegli la lingua"},
    {"login.english","Inglese"},
    {"login.italian","Italiano"}
    };
  }

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. You can compile the Java files from the command line with javac to obtain the two files MyBundle_en.class and MyBundle_it.class. Place both files inside the WEB INFclassesmyPkgi18n folder of your application’s root directory, as you would do with any other custom class.

Listing 4-19 shows the login page that supports two languages. To try it out, copy the folder named international from the software package for this chapter to webapps. Then type in your browser localhost:8080/international.

Listing 4-19. index.jsp of a Multilingual Application

01: <%@page language="java" contentType="text/html"%>
02: <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
03: <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
04: <c:set var="langExt" value="en"/>
05: <c:if test="${param.lang!=null}">
06:   <c:set var="langExt" value="${param.lang}"/>
07:   </c:if>
08: <fmt:setLocale value="${langExt}"/>
09: <fmt:setBundle basename="myPkg.i18n.MyBundle"
10:   var="lang" scope="session"/>
11: <html><head><title>i18n</title></head><body>
12: <h1><fmt:message key="login.loginmess" bundle="${lang}"/></h1>
13: <form method="post" action="home.jsp">
14:   <input name=id>
15:   <input name=passwd>
16:   <input type="submit"
17:     value="<fmt:message key="login.submit" bundle="${lang}"/>"
18:     >
19: <h2><fmt:message key="login.choose" bundle="${lang}"/></h2>
20: <a href="index.jsp?lang=en">
21:   <fmt:message key="login.english" bundle="${lang}"/>
22:   </a>
23: &nbsp;
24: <a href="index.jsp?lang=it">
25:   <fmt:message key="login.italian" bundle="${lang}"/>
26:   </a>
27: </body></html>

Lines 4-7 ensure that the page variable langExt is not null by setting it to en when the page is requested the first time. Line 8 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 9, 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.

After that, an element like the following one will insert in the appropriate language the message identified by the value of the key attribute:

<fmt:message key="keyName" bundle="${lang}"/>

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.

Figure 4-4 shows what the page looks like the first time you view it.

Image

Figure 4-4. The first time you view index.jsp

Figure 4-5 shows how the page looks when you choose Italian by clicking on the corresponding bottom link.

Image

Figure 4-5. The Italian version of index.jsp

If Tomcat cannot find a bundle, it will display the key name preceded and followed by three question marks, as shown in Figure 4-6. This indicates that you must have made a mistake in the directory names.

Image

Figure 4-6. index.jsp cannot find the messages.

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 non-standardized 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

Similar to fmt:SetBundle is fmt:bundle, but while you choose the basename the same way you do with fmt:setBundle, you cannot store your choice in a scoped variable. Instead, the basename you choose applies to all elements inside the body of fmt:bundle. Additionally, fmt:bundle also supports the attribute prefix, which extends the basename. For example, if you replace lines 9-10 in Listing 4-19 with

<fmt:bundle basename="myPkg.i18n.MyBundle" prefix="login.">

and insert </fmt:bundle> immediately above the last line, you then replace the existing line 21:

<fmt:message key="login.english" bundle="${lang}"/>

with:

<fmt:message key="english"/>

fmt:setTimeZone sets the current time zone to what specified in the attribute value, like in

<fmt:setTimeZone value="America/Los_Angeles"/>

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".

Both actions also support a pattern attribute that lets you specify in detail a custom format. Table 4-10 lists the available symbols.

Image

Image

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

Like fmt:parseNumber and fmt:formatNumber, fmt:ParseDate and fmt:formatDate can store their result into a scoped variable or send it to the output. Also, fmt:formatDate is bodyless while fmt:parseDate can be either bodyless or bodied. Not surprisingly, both actions support the timeZone attribute, and the format attributes dateStyle (with possible values "full", "long", "medium", "short", or "default"), and timeStyle (ditto). The Date-actions also support a pattern attribute. See Table 4-11 for a list of available symbols.

Image

Image

Additionally, fmt:parseDate supports the attributes parseLocale, and fmt:formatDate supports type (with possible values "date", "time", and "both").

With the remaining i18n action, fmt:requestEncoding, you specify what character encoding you expect for the text that the user types in forms. For example:

<fmt:requestEncoding value="UTF-8"/>

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.124.145