Using JSTL

The problem with EL and standards actions is that they have no flow control statements for example what if you wanted to loop through an array, in a scriptlet you would use a for loop but you are trying to get away from scripting, what you need is custom tags. They are as easy to use just like standard actions and there are libraries of these on the web, so you don't have to reinvent the wheel as someone has probably written one already, these are bundled into JSP Standard Tag Library (JSTL).

There is a standard library of custom tags known as the JSP Standard Tag Library (JSTL 1.2), what I am going to cover is the core JSTL tags, with these and EL you will probably find that is all you will need.

One thing you will notice with the tags is they start with a c: but this can be anything, the c stands for core by the way. Its a prefix to give to the Container the namespace for that tag or function name.

The first tag is the <c:out> tag it allows you to to display XML entities and not render them, you can use the escapeXML attribute, setting this true means that any XML will be converted to something the web browser will render, angle brackets and all

<c:out>

<c:set var="varTest1" value="This is an example" />
<c:out value='varTest1' escapeXml='true' />

Note:
true = declare the conversion of XML entities, this is the default
false = declare NO conversion of XML entities

The conversion only affects five characters

Character
Character Entity Code
<
&lt;
>
&gt;
&
&amp;
'
'
"
"

EL is exposed to some security risks called cross-site hacking and cross-site scripting, the attack is sent from one user to another user's browser using your web page as a delivery mechanism. What the cracker does is enter a comment field in your webapp, which is stored in the database, the cracker includes viral JavaScript code in the comment, the innocent user views the crackers comment which includes the dodgy Javascript and then the script runs comprising the users computer.

cross-site hacking and cross-site scripting This is a comment field <script> some dodgy Javascript code here</script> the comment continues

The <c:out> tag prevents this from happening because it displays the <script> tags instead of rendering them. The tag will also displays null values as blank text just like EL does. you can also have a default value if the attribute is null

default value <b>Hello <c:out value='${user}' default='guest' />.</br>

Looping

There is a looping tag you can use <c:forEach>, I have only given you the basic but there are other attributes like begin, end, step and skip within the forEach tag, just remember that the forEach loop obeys the same Java scoping rules, variables that the forEach tag creates are only accessible within the loop.

foreach <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><body>
  ...
  <c:forEach var="i" begin="1" end="10" >
    <c:out value="${i}" />
  </c:forEach>
  ...
</body></html>

Note: forEach loops can be nested if desired
loop counter <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><body>
  ...
  <c:forEach var="i" begin="1" end="10" varStatus="loopCounter">
    <c:out value="${i}" />
    <br />
    Count: ${loopCounter.count}
    <br>
  </c:forEach>
  ...
</body></html>

Note: the varStatus makes a new variable that holds an instance of javax.servlet.jsp.jstl.core.LoopTagStatus

If

The tag library has an if statement, however it does not have a else statement, see the choose statement below which replaces the traditional if/else statement

if <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><body>
  ...
  <c:forEach var="i" begin="1" end="10" varStatus="loopCounter">
    <c:out value="${i}" />
    <br />
    Count: ${loopCounter.count}
    <c:if test="${loopCounter.count=='5'}">
      <br>5 was selected
    </c:if>

    <br>
  </c:forEach>
  ...
</body></html>

Choose

The choose tag is the replace for an if/else statement, but its not like a switch/case statement because there is no fall through

choose <c:choose>
  <c:when test="${loopCounter.count=='1'}">
    You got Gold
  </c:when>

  <c:when test="${loopCounter.count=='2'}">
    You got Silver
  </c:when>

  <c:when test="${loopCounter.count=='3'}">
    You got Bronze
  </c:when>

  <c:otherwise>
    Better luck next time
  </c:otherwise>
</c:choose>

Set

The <jsp:setproperty> tag can only do one thing, set a property of a bean, with a <c:set> tag you can set a property in a Map, you can make a new entry in a Map, you can even create a new request-scoped attribute. There is always a but, you need to know some simple rules that the <c:set> tag has, first it comes in two flavors

Both versions cab either use a body or not, remember that if a attribute evaluates to null, the variable will be removed.

var version
NO body

<c:set var="userLevel" scope="session" value="Cowboy" />
<c:set var="will" value="${movie.comedy}" />

Variable: ${userLevel}

Note: if there's not a session-scoped attribute named "userLevel" the tag creates one, assuming the value attribute value is not null

with a body <c:set var="userLevel" scope="session" >
  Sheriff,Bartender,Cowgirl
</c:set>

Note: no / at the end of the first statement
target version
NO body <c:set target="${PetMap}" property="dogName" value="Rover" />
with a body <c:set target="${PetMap}" property="dogName" >
  ${dog.name}
</c:set>

There are a number of keys points to remember with the <c:set> tag

Remove

The <c:remove> tag removes values

remove <c:set var="userStatus" scope="request value="Brilliant" />

<c:remove var="userStatus" scope="request" />

Import

The <c:import> tag allows you another way to add content from another resource into a JSP, the different between the tag version and the include directive and the <jsp:include> standard action is that the tag version can import stuff from outside the Container, remember though it should be a HTML fragment (it should not have the <html><body> parts) not a full HTML file.

import <c:import url="http://www.datadisk.co.uk/images/photo.gif" />

As a quick reference here is a what each include content uses

to include content

# Include Directive
<%@ include file="Header.html %>

# Standard Action
<jsp:include page="Header.jsp" />

# JSTL tag
<c:import url="header.html" />

Note: remember that the header file should not contain <html><body> elements

the tag version can also customize the thing that you include just like the stand action version can

customizing # Header.jsp

<em><strong>${param.subTitle}</strong></em>

# JSP with the import
<c:import url "Header.jsp" >
  <c:param name="subTitle" value="customizing the STL tag" />
/c:import>

Url

When a client obtains a session you get a session ID along with your session, this allows the Container to identify the client with a particular session, I have discussed this in my session management section, if the user has turned off cookie acceptance you normally revert to using URL rewriting but with servlets you still have to encode your URLs (you tell the Container to append your jsessionid to the URL). This can also be achieved using the <c:url> tag

url rewriting <c:url value='/inputComments.jsp' />

Note: the above adds the jsessionid to the end of the value relative URL (if cookies has been disabled)

Remember that in an HTTP GET request, the parameters are appended to the URL as a query string, but an HTTP request won't work correctly if it conationsunsafe characters (most modern browsers can now cope with this). URL are often encoded which means replacing the unsafe/reserved characters with other characters and then the whole thing is decoded on the server side. For an example spaces are not allowed in URLs so you can substitute a plus sign "+" for the space. The problem is the <c:url> tag does not automatically encode your URLs. To get around this problem you have to use the body of the <c:url> tag

using the url body

<c:url value='/inputComments.jsp' var="url" >
  <c:param name="firstName" value="Paul" />
  <c:param name="lastname" value="Valle" />
</c:url>

<br>The resulting url is:
<c:out value="{url}"/>

Note: the param takes care of the encoding for us

inputComments.jsp?firstName=Crouching+Pixels&lastName=Hidden+Cursor

Catch

The <c:catch> tag is like a try/catch in Java, you use it when you have a page that invokes a risky tag, but you think you can recover. The risky tag is wrapped around a <c:catch> tag, if a exception is thrown then your default error handling will kick in and the user will get the error page declared in the DD. The <c:catch> tag is a one-in-all try/catch (there's no try).

catch

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

<html><body>

<h1> Error Page </h1>

<c:catch var="dividebyzero">
  <% int x = 10/0; %>
</c:catch>

<c:if test="${dividebyzero}" />

</body></html>

to setup your default error page you must configure it in the DD (deployment descriptor)

default error page examples (web.xml)

<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/errorPage.jsp</location>
</error-page>

<error-page>
  <exception-type>java.lang.ArithmeticException</exception-type>
  <location>/arithmeticError.jsp</location>
</error-page>

<error-page>
  <error-code>404</error-code>
  <location>/notFoundError.jsp</location>
</error-page>

JSP errorPage (errorPage.jsp)

<%@ page isErrorPage="true" %>

<html><body>

<strong>You caused a ${pageContext.exception.message} on the server </strong>

<c:forEach var="st" items="${pageContext.exception.stackTrace}">
  ${st}
</c:forEach>

</body></html>

Note: you can get a more detailed error message using the exception object, you can only use it if the page is an official error page

Note: when using Tomcat 6 I experienced problems with getting the ${pageContext.exception} to display, I had to leave out the isErrorPage tag in the errorPage.jsp file but most books and web site say you should have it in?

Other Libraries

The JSTL is hugh, version 1.2 has five libraries, four with custom tags and one with a bunch of functions for String manipulation, see the Java web site for more details

Custom Tags

I am going to cover a whole section on how to create a custom tag library, this section explains what to do if someone hands you an already created custom tag library, the main items that you need to know are

tag name and syntax The tag has a name, for an example the <c:set> tag as a prefix of c (this can be anything actually) and a tag name of set. The name comes from the TLD (Tag Library Descriptor). The syntax includes things like required and optional attributes, whether the tag can have a body, the type of each each attribute and whether the attribute can be an expression (vs a literal String)
library URI The URI is a unique identifier in the Tag Library Descriptor, the URI is what you put in your taglib directive. It's what tells the Container how to identify the TLD file within the web app, which the Container needs in order to map the tag name used i the JSP to the Java code that runs when you use the tag .

The TLD describes two main things: custom tags and EL functions.

TLD

<?xml version="1.0" encoding"ISO-8859-1" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation=http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">

  <tlib-version>1.2</tlib-version>

  <short-name>RandomTags</short-name>
  <function>
    <name>rollit</name>
    <function-class>foo.DiceRoller</function-class>
    <function-signature>int rollDice()</function-signature>
  </function>

  <uri>randomThings</uri>

  <tag>
    <description>random advice</description>
    <name>advise</name>
    <tag-class>foo.AdvisorTagHandler</tag-class>
    <body-content>empty</body-content>

    <attribute>
      <name>user</name>
      <required>true</required>
      <rtexprvalue>true<rtexprvalue>
    </attribute>
  </tag>
</taglib>

Note:

<tlib-version> - mandatory

<short-name> - mandatory mainly for tools to use

<description> - optional but a good idea

<name> - required, this is what you use insde the tag <c:<name>>

<tag-class> - required this is what the Container calls when someone uses the tag

<body-content> - required this states if there should be something in the body, there can be three options (see                  below for more options

<attribute> - if the tag has attributes then one <attribute> per tag attribute is required

<name><required> - means you must put a attribute in the tag

<rtexprvalue> - means the attribute can be a RunTimeExpressionValue (does not have have to be a String value),                 the default is false which means it has to be a String, true means it does not have to be a                 String

using the above tag

<html<body>

<%@ taglib prefix="mine" uri="randomThings" %>

Advisor Page<br>

<mine:advice user="${userName}" />

</body></html>

Note: you might see uri="http://java.sun.com/jsp/jstl/core" remember this is just a name not a URL, it just points to the core library

the Java class to do the tag work

package foo;

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

public class AdvisorTagHandler extends SimpleTagSupport {

  private String user;

  public void doTag() throws JspException, IOException {
    getJspContext().getOut().write("hello" + user + "<br>" );
    getJspContext().getOut().write("Your advise is: " + "have a happy life" );
  }

  public void setUser(String user) {
    this.user = user;
  }
}

Note: the Container will call the doTag() method when the JSP invokes the tag using the name declared in the TLD, to set the value it uses JavaBean property naming conventions to figure out that a "user" attribute should be sent to the setUser method

The value attribute can be three kinds

attribute value

# EL expression
<mine:advice user="${userName}" />

# Scripting Expression
<mine:advice user='<%= request.getAttribute("username") %>' />

# Standard action
<mine:advise>
  <jsp:attribute name="user">${userName}</jsp:attribute>
</mine:advise>

Note: you can put one jsp action in a tag even when it is declared empty

The <body-content> element can have four options

<body-content> element
empty the tag must not have a body
scriptless The tag must not have scripting elements (scriptlets, scripting expressions and declarations) but it can have template text and EL and custom and standard actions
tagdependent The tag body is treated as plain text, so the EL is not evaluated and tags/actions are not triggered
JSP The tag body can have anything that can go inside a JSP
Three ways to invoke a tag that cannot have a body
  # Empty tag
<mine:advise user="${username}" />

# A tag with nothing between the opening and closing tags

<mine:advise user="${username"></mine:advise>

# A tag with only the <jsp> tags between opening and closing tags
<mine:advise>
  <jsp:attribute name="user">${userName}</jsp:attribute>
</mine:advise>

The container will look for TLDs in four places

  • Directory inside the WEB-INF
  • Directory inside a sub-directory of WEB-INF
  • Inside the META-INF directory inside a JAR file that's inside WEB-INF/lib
  • Inside a sub-directory of META-IN inside a JAR file that's inside WEB-INF/lib