Sans Pareil Technologies, Inc.

Key To Your Business

Lab 4: JSP EL and JSTL


An expression language makes it possible to easily access application data stored in JavaBeans components. For example, the JSP expression language allows a page author to access a bean using simple syntax such as ${name} for a simple variable or ${name.foo.bar} for a nested property.

Implicit Objects


The JSP expression language defines a set of implicit objects:
  • pageContext: The context for the JSP page. Provides access to various objects including:
  • servletContext: The context for the JSP page's servlet and any web components contained in the same application.
  • session: The session object for the client.
  • request: The request triggering the execution of the JSP page.
  • response: The response returned by the JSP page.
In addition, several implicit objects are available that allow easy access to the following objects:
  • param: Maps a request parameter name to a single value
  • paramValues: Maps a request parameter name to an array of values
  • header: Maps a request header name to a single value
  • headerValues: Maps a request header name to an array of values
  • cookie: Maps a cookie name to a single cookie
  • initParam: Maps a context initialization parameter name to a single value
Finally, there are objects that allow access to the various scoped variables
  • 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
  • applicationScope: Maps application-scoped variable names to their values
When an expression references one of these objects by name, the appropriate object is returned instead of the corresponding attribute. For example, ${pageContext} returns the PageContext object, even if there is an existing pageContext attribute containing some other value.

Gradle

Modify the default created gradle file to match the following:

group 'mis283'

apply plugin: 'java'
apply plugin: 'war'
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

gretty {
    httpPort = 8081
    integrationTestTask = 'test'
}

dependencies {
    compile 'javax.servlet:jstl:1.2'
    compile 'javax.mail:mail:1.5.0-b01'
    compile 'javax.servlet:jstl:1.2'
    compile 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
    testCompile 'junit:junit:4.12'
    testCompile 'org.httpunit:httpunit:1.7.2'
}

CSS

Use the following style file for this lab session.

body {
    font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
    margin-left: 5em;
    margin-right: 5em;
}

label {
    float: left;
    font-weight: bold;
    width: 10em;
}

input[type="text"], input[type="password"], input[type="email"] {
    width: 20em;
    margin-left: 0.5em;
    margin-bottom: 0.5em;
}

input[type="submit"] {
    margin-left: 0.5em;
    margin-bottom: 0.5em;
}

table {
    border: 1px solid black;
    border-collapse: collapse;
}

th, td {
    border: 1px solid black;
    text-align: left;
    padding: 0.5em;
}

.bold {
    font-weight: bold;
}

#warning {
    font-size: 80%;
    color: tan;
}

Obsolete JSP

We will look at primitive JSP that contains scriptlets, methods and code that should never be in a JSP. Add a file named bad.jsp under src/main/webapp.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.text.SimpleDateFormat, java.util.Date, java.util.Enumeration" %>
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <title>Time Of Day</title>
  </head>
  <body>
    <h1>Time of day</h1>
    <label>ISO 8601 date time:</label><aside id="dateTime"><%= dateTime() %></aside>
    <label>ISO 8601 date:</label><aside id="date"><%= date() %></aside>
    <label>Year:</label><aside id="year"><%= year() %></aside>
    <label>Month:</label><aside id="month"><%= month() %></aside>
    <label>Day:</label><aside id="day"><%= day() %></aside>
    <label>Time:</label><aside id="time"><%= time() %></aside>
    <label>Local date time:</label><aside id="local"><%= localDate() %></aside>
    <label>Numbers:</label><aside id="numbers"><% for ( int i = 0; i < 10; ++i ) { %><%= i %>, <% } %></aside>
    <table>
    <%
        final Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            final String name = (String) headerNames.nextElement();
            final String value = split(request.getHeader(name));
    %>
      <tr><td><%= name %></td><td><%= value %></td></tr>
    <%
        }
    %>
    </table>
  </body>
</html>

<%!
    private String year() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String month() {
        final SimpleDateFormat sdf = new SimpleDateFormat("MMMMM");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String day() {
        final SimpleDateFormat sdf = new SimpleDateFormat("dd");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String date() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String time() {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSXXX");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String dateTime() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        final Date date = new Date();
        return sdf.format(date);
    }

    private String localDate() { return new Date().toString(); }

    private String split(String value) {
        final StringBuilder sb = new StringBuilder(value.length() + 32);
        final String[] parts = value.split(";");
        for (final String part : parts) {
            sb.append(part);
            if (!part.equals(parts[parts.length-1])) sb.append("<br/>");
        }
        return sb.toString();
    }
%>



We can adapt the unit test from lab2 to do some validation on the output of the JSP page. Create mis283.BadTest under src/test/java

package mis283;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
import org.junit.BeforeClass;
import org.junit.Test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class BadTest {
    private static final Map<String, String> components = new HashMap<>();
    private static WebConversation conversation;
    private static WebRequest request;
    private static WebResponse response;

    static {
        final Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        components.put("year", sdf.format(date));

        sdf = new SimpleDateFormat("MM");
        components.put("month", sdf.format(date));

        sdf = new SimpleDateFormat("MMMMM");
        components.put("Month", sdf.format(date));

        sdf = new SimpleDateFormat("dd");
        components.put("day", sdf.format(date));

        sdf = new SimpleDateFormat("HH");
        components.put("hour", sdf.format(date));

        sdf = new SimpleDateFormat("EEE");
        components.put("dayOfWeek", sdf.format(date));
    }

    @BeforeClass
    public static void init() throws Exception {
        conversation = new WebConversation();
        request = new GetMethodWebRequest("http://localhost:8081/lab4/bad.jsp");
        response = conversation.getResponse( request );
    }

    @Test
    public void dateTime() throws Exception {
        final String value = response.getElementWithID("dateTime").getText();
        assertTrue("Wrong year in response", value.startsWith(components.get("year")));
        assertTrue("Wrong month in response", value.contains(format("-%s-", components.get("month"))));
        assertTrue("Wrong day in response", value.contains(format("-%sT", components.get("day"))));
        assertTrue("Wrong hour in response", value.contains(format("T%s:", components.get("hour"))));
    }

    @Test
    public void date() throws Exception {
        final String value = response.getElementWithID("date").getText();
        assertTrue("Wrong year in response", value.startsWith(components.get("year")));
        assertTrue("Wrong month in response", value.contains(format("-%s-", components.get("month"))));
        assertTrue("Wrong day in response", value.endsWith(components.get("day")));
    }

    @Test
    public void year() throws Exception {
        final String value = response.getElementWithID("year").getText();
        assertEquals("Wrong year in response", value, components.get("year"));
    }

    @Test
    public void month() throws Exception {
        final String value = response.getElementWithID("month").getText();
        assertEquals("Wrong month in response", value, components.get("Month"));
    }

    @Test
    public void day() throws Exception {
        final String value = response.getElementWithID("day").getText();
        assertEquals("Wrong day in response", value, components.get("day"));
    }

    @Test
    public void time() throws Exception {
        final String value = response.getElementWithID("time").getText();
        assertTrue("Wrong hour in response", value.startsWith(components.get("hour")));
    }

    @Test
    public void local() throws Exception {
        final String value = response.getElementWithID("local").getText();
        assertTrue("Wrong day of week in response", value.startsWith(components.get("dayOfWeek")));
    }
}

Bean Class

We will create a JavaBean style utility class that will provide the date functions we need to access in our JSP. Create a Java class mis283.DateUtil under src/main/java

package mis283;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {
    public String getYear() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getMonth() {
        final SimpleDateFormat sdf = new SimpleDateFormat("MMMMM");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getDay() {
        final SimpleDateFormat sdf = new SimpleDateFormat("dd");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getDate() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getTime() {
        final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSSXXX");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getDateTime() {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
        final Date date = new Date();
        return sdf.format(date);
    }

    public String getLocalDate() { return new Date().toString(); }
}

Modern JSP

We can reimplement the JSP using Expression Language (EL) and JSTL as follows. Note that we are using the DateUtil bean class to provide the date information to be displayed on screen.

This site gives a good tutorial to JSTL and the Functions tag library available for use with JSP.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="dateUtil" scope="page" class="mis283.DateUtil"/>
<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <title>Time Of Day</title>
  </head>
  <body>
    <h1>Time of day</h1>
    <label>ISO 8601 date time:</label><aside id="dateTime">${dateUtil.dateTime}</aside>
    <label>ISO 8601 date:</label><aside id="date">${dateUtil.date}</aside>
    <label>Year:</label><aside id="year">${dateUtil.year}</aside>
    <label>Month:</label><aside id="month">${dateUtil.month}</aside>
    <label>Day:</label><aside id="day">${dateUtil.day}</aside>
    <label>Time:</label><aside id="time">${dateUtil.time}</aside>
    <label>Local date time:</label><aside id="local">${dateUtil.localDate}</aside>
    <label>Numbers:</label><aside id="numbers"><c:forEach begin="0" end=“9” varStatus="loop">${loop.index}, </c:forEach></aside>
    <table>
      <c:forEach items="${headerValues}" var="keyValue">
      <tr>
        <td>${keyValue.key}</td>
        <td><c:forEach items="${keyValue.value}" var="headerValue">
          <c:forTokens items="${headerValue}" delims=";" var="value"><c:out value="${value}"/></br></c:forTokens>
        </c:forEach></td>
      </c:forEach>
    </table>
  </body>
</html>

Tests

We can refactor the test we used for bad.jsp and make it run against both the JSP files. We will extract a Base super class, from which we will inherit BadTest and GoodTest.

Base

package mis283;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
import org.junit.BeforeClass;
import org.junit.Test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public abstract class Base {
    private static final Map<String, String> components = new HashMap<>();
    protected static WebConversation conversation;
    protected static WebRequest request;
    protected static WebResponse response;

    static {
        final Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        components.put("year", sdf.format(date));

        sdf = new SimpleDateFormat("MM");
        components.put("month", sdf.format(date));

        sdf = new SimpleDateFormat("MMMMM");
        components.put("Month", sdf.format(date));

        sdf = new SimpleDateFormat("dd");
        components.put("day", sdf.format(date));

        sdf = new SimpleDateFormat("HH");
        components.put("hour", sdf.format(date));

        sdf = new SimpleDateFormat("EEE");
        components.put("dayOfWeek", sdf.format(date));
    }

    @Test
    public void dateTime() throws Exception {
        final String value = response.getElementWithID("dateTime").getText();
        assertTrue("Wrong year in response", value.startsWith(components.get("year")));
        assertTrue("Wrong month in response", value.contains(format("-%s-", components.get("month"))));
        assertTrue("Wrong day in response", value.contains(format("-%sT", components.get("day"))));
        assertTrue("Wrong hour in response", value.contains(format("T%s:", components.get("hour"))));
    }

    @Test
    public void date() throws Exception {
        final String value = response.getElementWithID("date").getText();
        assertTrue("Wrong year in response", value.startsWith(components.get("year")));
        assertTrue("Wrong month in response", value.contains(format("-%s-", components.get("month"))));
        assertTrue("Wrong day in response", value.endsWith(components.get("day")));
    }

    @Test
    public void year() throws Exception {
        final String value = response.getElementWithID("year").getText();
        assertEquals("Wrong year in response", value, components.get("year"));
    }

    @Test
    public void month() throws Exception {
        final String value = response.getElementWithID("month").getText();
        assertEquals("Wrong month in response", value, components.get("Month"));
    }

    @Test
    public void day() throws Exception {
        final String value = response.getElementWithID("day").getText();
        assertEquals("Wrong day in response", value, components.get("day"));
    }

    @Test
    public void time() throws Exception {
        final String value = response.getElementWithID("time").getText();
        assertTrue("Wrong hour in response", value.startsWith(components.get("hour")));
    }

    @Test
    public void local() throws Exception {
        final String value = response.getElementWithID("local").getText();
        assertTrue("Wrong day of week in response", value.startsWith(components.get("dayOfWeek")));
    }
}


BadTest

package mis283;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import org.junit.BeforeClass;

public class BadTest extends Base {
    @BeforeClass
    public static void init() throws Exception {
        conversation = new WebConversation();
        request = new GetMethodWebRequest("http://localhost:8081/lab4/bad.jsp");
        response = conversation.getResponse( request );
    }
}



GoodTest

package mis283;

import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import org.junit.BeforeClass;

public class GoodTest extends Base {
    @BeforeClass
    public static void init() throws Exception {
        conversation = new WebConversation();
        request = new GetMethodWebRequest("http://localhost:8081/lab4/good.jsp");
        response = conversation.getResponse( request );
    }
}

Custom Tag

We will develop a custom tag that will display the details relevant to a Person class (the same that was used in lab3). Copy over the class and the servlet for handling form posts along with the html and jsp files and test class from lab3. You will need to modify the PersonCreateTest with the new URL for lab4.

Modify the personView.jsp as follows:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="lab4" uri="http://morainevalley.edu/mis283/tlds/person" %>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="../styles.css"/>
    <title>Person - ${person.name}</title>
  </head>
  <body>
  <section>
    <h1>Newly created Person</h1>
    <lab4:person bean="${person}"/>
  </section>
  <footer>
    Demo application that illustrates JSP EL and custom tags
  </footer>
  </body>
</html>



We will now write our tag library descriptor (tld) file.

Create a directory named WEB-INF under src/main/webapp and create a file named person.tld under it.

<taglib version="2.0" xmlns="http://java.sun.com/xml/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
    <tlib-version>2.1</tlib-version>
    <jsp-version>2.2</jsp-version>
    <short-name>Person Tag</short-name>

    <tag>
        <name>person</name>
        <tag-class>mis283.tag.PersonTag</tag-class>
        <body-content>empty</body-content>

        <attribute>
            <name>bean</name>
            <required>true</required>
            <type>mis283.model.Person</type>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>


Create the web application deployment descriptor file web.xml under the same directory.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <display-name>JSP EL and Custom Tags</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <jsp-config>
        <taglib>
            <taglib-uri>http://morainevalley.edu/mis283/tlds/person</taglib-uri>
            <taglib-location>/WEB-INF/person.tld</taglib-location>
        </taglib>
    </jsp-config>
</web-app>

Tag

Create the mis283.tag.PersonTag class under src/main/java as follows.

package mis283.tag;

import mis283.model.Person;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;

import static mis283.model.Person.notEmpty;

public class PersonTag extends SimpleTagSupport {
    private Person bean;

    @Override
    public void doTag() throws JspException, IOException {
        final JspWriter writer = getJspContext().getOut();
        writeId(writer);
        writeFirstName(writer);
        writeMiddleName(writer);
        writeLastName(writer);
        writeEmail(writer);
        writeUsername(writer);
    }

    public void setBean(Person bean) {
        this.bean = bean;
    }

    private void writeId(final JspWriter writer) throws IOException {
        writer.print("<label>Person ID:</label><q id=\"id\">");
        writer.print(bean.getId());
        writer.println("</q><br/>");
    }

    private void writeFirstName(final JspWriter writer) throws IOException {
        writer.print("<label>First Name:</label><q id=\"firstName\">");
        writer.print(bean.getFirstName());
        writer.println("</q><br/>");
    }

    private void writeMiddleName(final JspWriter writer) throws IOException {
        if (notEmpty(bean.getMiddleName())) {
            writer.print("<label>Middle Name:</label><q id=\"middleName\">");
            writer.print(bean.getMiddleName());
            writer.println("</q><br/>");
        }
    }

    private void writeLastName(final JspWriter writer) throws IOException {
        writer.print("<label>Last Name:</label><q id=\"lastName\">");
        writer.print(bean.getLastName());
        writer.println("</q><br/>");
    }

    private void writeEmail(final JspWriter writer) throws IOException {
        writer.print("<label>E-mail:</label><q id=\"email\">");
        writer.print(bean.getEmail());
        writer.print("</q><br/>");
    }

    private void writeUsername(final JspWriter writer) throws IOException {
        writer.print("<label>Username:</label><q id=\"username\">");
        writer.print(bean.getUsername());
        writer.println("</q><br/>");
    }
}