8. Developing JSP pages using custom tags



Custom Tags Tutorial

8.1 Relate the JSTL to common job roles in web application development and understand the use of tags in JSP development

As per 2.2 Describe essentials of JSPs


8.2 Recognize correct syntax for tags

Syntax for the 3 types of tags is covered under 8.4 Write JSP code using several standard tags

TLD directory locations

  1. Directly inside WEB-INF
  2. Directly inside a subdirectory of WEB-INF
  3. Inside the META-INF directory inside a JAR file that's inside WEB-INF/lib
  4. Inside a sub-directory of META-INF inside a JAR file that's inside WEB-INF/lib


8.3 Configure a JSP to use tags from the JSTL

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


8.4 Write JSP code using several standard tags

Four types of tags:

  1. Tag Files (since 2.0)
  2. Simple Tags (since 2.0)
  3. EL function tags
  4. Classic
  • Tag files implement the tag functionality with another page (using JSP). They do not require an entry in the Deployment Descriptor.
  • Tag handlers (simple and classic) implement the tag functionality with a special Java class.
  • All tag handlers are thread-safe
  • You cannot embed one tag inside another tag's attribute lis

web.xml

Except for tag files, tag libraries require an entry in web.xml:

<taglib id="myLibrary">
    <taglib-uri>myTags</taglib-uri>
    <taglib-location>/WEB-INF/myTags.tld</taglib-location>
</taglib>

id attribute is optional

Tag files

  1. Take a standard JSP file and rename it to myTag.tag
  2. Place tag file in WEB-INF/tags (note: TLD files are not allowed in this folder)
  3. In another JSP, use the following:
<%@ taglib prefix="tagFile" tagdir="/WEB-INF/tags" %>
<html>
<body>
    <tagFile:myTag/>
</body>
</html>

A JSP 2.0+ compliant container will automatically generate an implicit tag library (TLD) for a set of tag files.

Tag file locations

  • WEB-INF/tags or subdirectory (does not require TLD)
  • META-INF/tags or subdirectory inside a JAR file (requires TLD for tag file)

Passing attributes

Tag files can use attribute directive exclusively.

Enclosing JSP:

<tagFile:myTag text="This is text" />

Tag file:

<%@ attribute name="text" required="true" rtexprvalue="true" %>
 
<em><strong>${text}</strong></em> <br>

Passing a body

  • Use <jsp:doBody/> in tag
  • Use body-content attribute in enclosing JSP
  • Cannot use scripting in the body, but can specify: empty or tagdependent (plain text)

Enclosing JSP:

<%@ taglib prefix="tagFile" tagdir="/WEB-INF/tags" %>
<html>
<body>
    <tagFile:bodyTag>
        stuff
    </tagFile:bodyTag>
</body>
</html>

Tag file:

<%@ tag body-content="tagdependent" %>
 
<em><strong> <jsp:doBody/> </strong></em> <br/>

dynamic-attributes

The user of the tag may send extra attributes that are not explicitly defined with a directive. These attributes are called dynamic ones. The value of dynamic-attributes holds a Hashmap of the dynamic attributes.

Enclosing JSP:

<%@ taglib prefix="tagFile" tagdir="/WEB-INF/tags" %>
<html>
  <body>
    <tagFile:dynamicAttributes name="text" java="100%" />
  </body>
</html>

Tag file:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ tag dynamic-attributes="map" %>
<p>These are the dynamic attributes:</p>
<ol>
  <c:forEach var="item" items="${map}">
    <li>${item.key}</li>
 </c:forEach>
</ol>

More is covered on dynamic attributes with SimpleTagSupport

Simple tag handler

7GffIuf.png
  1. Write a class that extends SimpleTagSupport
  2. Override the doTag() method
  3. Create a TLD for the tag
  4. Deploy the tag handler and TLD
  5. Write a JSP that uses the tag

Tag with no body

This example has no body and uses SimpleTagSupport. A few notes:

  • If you try to use a body with an empty tag, you will get a runtime exception.
  • throw new SkipPageException() to skip processing after the tag
  • rtexprvalue include EL expresisons, scripting and standard actions
  • <body-content> may be: empty, scriptless, tagdependent, or JSP
    • empty: single tag with slash, or opening/closing tags with nothing in between, or only a <jsp:attribute> tag
    • scriptless: can only have template text, EL, custom and standard actions
    • tagdpendent: tag body is treated as plain text, so EL not evaulated and tags/actions are not triggered
    • JSP: anything that normally can go inside a JSP

TLD file:

<?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/webjsptaglibrary_2_0.xsd" version="2.0">
    <tlib-version>1.2</tlib-version>
    <short-name>RandomTags</short-name>
    <uri>randomThings</uri>
    <tag>
        <description>random advice</description> <!-- optional -->
        <name>advice</name>
        <tag-class>com.example.tags.AdvisorTagHandler</tag-class>
        <body-content>empty</body-content> <!-- nothing in-between tags -->
        <attribute>
            <name>user</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue> <!-- doesn't have to be String literal, default is false -->
        </attribute>
    </tag>
</taglib>

Enclosing JSP:

<%@ taglib prefix="el" uri="randomThings"%>
<el:advice user="${userName}" />

Tag handler:

package com.example.tags;
 
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 advice is: " + getAdvice() );
    }
 
    public void setUser(String user) {
        this.user = user;
    }
 
    public String getAdvice() {
        String[] adviceStrings = {"That color's not working for you.",
                "You should call in sick.", "You might want to rethink that haircut."};
 
        int random = (int) (Math.random() * adviceStrings.length);
        return adviceStrings[random];
    }
}

Tag with body

Process the body of the tag and print it to the response. The null argument means the output goes to the response rather than some OTHER writer you pass in.

TLD file extract:

<tag>
    <description>simple tag with a body</description>
    <name>simplebody</name>
    <tag-class>com.example.tags.SimpleBodyTagHandler</tag-class>
    <body-content>scriptless</body-content>
</tag>

JSP file:

<%@ taglib prefix="simpleTag" uri="randomThings"%>
<simpleTag:simplebody>
    body text
</simpleTag:simplebody>

Tag handler:

package com.example.tags;
 
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
 
public class SimpleBodyTagHandler extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        getJspBody().invoke(null);
    }
}

dynamic-attributes

  • The DynamicAttributes interface allows the tag handler class to accept any number of tag attributes.
  • The tag declaration in the TLD must include the <dynamic-attributes> element.
  • Explicit tag attributes must have a setter method.
  • Typically, you will use a hashmap to store the dynamic attribute name/value pairs using the setDynamicAttribute() method.

TLD:

<tag>
  <name>simple</name>
  <tag-class>my.Simple</tag-class>
  <body-content>empty</body-content>
  <dynamic-attributes>true</dynamic-attributes>
</tag>

Enclosing JSP:

<%@ taglib prefix="yo" uri="nikojava" %>
<html>
  <body>
    <yo:simple friend="Niko" casoline="soft" more="lagi" />
  </body>
</html>

Tag handler:

public class Simple extends SimpleTagSupport implements DynamicAttributes {
  private Map<String, Object> map = new HashMap<String, Object>();
  public void setDynamicAttribute(String uri, String name, Object value) {
    map.put(name, value);
  }
  public void doTag() throws IOException, JspException {
    JspWriter out = getJspContext().getOut();
    out.append("These are the dynamic attributes:");
    out.append("<ul>");
    for (Map.Entry<Stringbject> element : map.entrySet()) {
      out.append("<li>");
      out.append(element.getKey() + " &rArr; " + element.getValue());
      out.append("</li>");
    }
    out.append("</ul>");
  }
}

EL function tag: DiceFunctions

Class with static method

package com.example.model;
 
public class DiceRoller {
 
    public static int rollDice() {
        return (int) ((Math.random() * 6) + 1);
    }
}

TLD file

The TLD filename is not important, only the URI.

<?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/webjsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.2</tlib-version>
<uri>DiceFunctions</uri>
  <function>
    <!-- The name can be different to the function signature name. 
        Also, remember that is called "name", rather than "function-name" -->
    <name>rollIt</name>
    <function-class>com.example.model.DiceRoller</function-class>
    <function-signature>
      int rollDice()
    </function-signature>
</function>
</taglib>

JSP

<%@ taglib pref x="mine" uri="DiceFunctions"%>
<html>
<body>
    ${mine:rollIt()}
</body>
</html>

Classic tag handlers

l9J1nkd.png

There are 3 main methods, which can return 2 different values.

  • doStartTag()
    • SKIP_BODY [default] = go on, do not evaluate the body of the tag.
    • EVAL_BODY_INCLUDE = evaluate the body once
  • doAfterBody() [from IterationTag interface]
    • SKIP_BODY [default, if preceeded by EVAL_BODY_INCLUDE] = just go on, stop evaluating the body.
    • EVAL_BODY_AGAIN = evaluate the body again.
  • doEndTag()
    • SKIP_PAGE = skip the rest of the page.
    • EVAL_PAGE [default] = proceed normally to the rest of the page.

doStartTag() and doEndTag() always run exactly once, but doAfterBody() may run 0 or more times.

<%@ taglib prefx="mine" uri="KathyClassicTags" %>
<html>
<body>
    Classic Tag One:<br>
    <mine:classicOne />
</body>
</html>
<tag>
    <description>ludicrous use of a Classic tag</description>
    <name>classicOne</name>
    <tag-class>foo.Classic1</tag-class>
    <body-content>empty</body-content>
</tag>
public class Classic1 extends TagSupport {
    public int doStartTag() throws JspException {
        JspWriter out = pageContext.getOut();
        try {
            out.println("classic tag output");
        } catch(IOException ex) {
            throw new JspException("IOException- " + ex.toString());
        }
        return SKIP_BODY;
    }
}

This example uses doEndTag()

public class Classic2 extends TagSupport {
    JspWriter out;
 
    public int doStartTag() throws JspException {
        out = pageContext.getOut();
        try {
            out.println("in doStartTag()");
        } catch(IOException ex) {
            throw new JspException("IOException- " + ex.toString());
        }
        return SKIP_BODY;
    }
 
    public int doEndTag() throws JspException {
        try {
            out.println("in doEndTag()");
        } catch(IOException ex) {
            throw new JspException("IOException- " + ex.toString());
        }
        return EVAL_PAGE;
    }
}

Example evaluating the body

public class ClassicTest extends TagSupport {
    JspWriter out;
 
    public int doStartTag() throws JspException {
        out = pageContext.getOut();
        try {
            out.println("Before body.");
        } catch(IOException ex) {
            throw new JspException("IOException- " + ex.toString());
        }
        return EVAL_BODY_INCLUDE;
    }
 
    public int doEndTag() throws JspException {
        try {
            out.println("After body.");
        } catch(IOException ex) {
            throw new JspException("IOException- " + ex.toString());
        }
        return EVAL_PAGE;
    }
}

BodyTag Interface

Allows you to manipulate the body. Adds two methods:

  • void doInitBody()
  • void setBodyContent(BodyContent)
and allows doStartTag() to return EVAL_BODY_BUFFERED


8.5 List capabilities of JSTL tags

The JSTL library is huge and you don't need to know them all for the exam. Only the core library is on the exam. Functionality offered includes:

  • Core library
  • Formatting library
  • SQL library
  • XML library

<c:catch>

  • not like try/catch
  • encloses code that may throw exception, processing continues after catch, error page not used
  • no exception object by default, use var attribute to get it. Must be used after the </c:catch>
<c:catch>
    <% int x = 10/0; %>
</c:catch>
Processing continues here
 
<%-- using var attribute --%>
<c:catch var="myException">
    Inside the catch...
    <% int x = 10/0; %>
</c:catch>
<c:if test="${myException != null}">
    There was an exception: ${myException.message} <br>
</c:if>

<c:choose>

<c:choose>
    <c:when test="${userPref == 'performance'}">
        Now you can stop even if you <em>do</em> drive insanely fast.
    </c:when>
    <c:when test="${userPref == 'safety'}">
        Our brakes will never lock up, no matter how bad a driver you are.
    </c:when>
    <c:when test="${userPref == 'maintenance'}">
        Lost your tech job? No problem--you won't have to service these brakes for at least three years.
    </c:when>
    <c:otherwise>
        Our brakes are the best.
    </c:otherwise>
</c:choose>

<c:forEach>

You can either iterate through a Collection or use it just like a regular for loop. It use the following attributes:

  • items : This is used if you are iterating through a Collection
  • begin and end : Used to specify the begin and end
  • step : used to skip items. For example, if step is 2, the loop will run for 1, 3, 5, 7 and so on.
  • var : This defines the loop variable. In this question, since the forEach block using ${i}, our loop variable is i. If you are iterating through a Collection, the type of this variable is same the objects of the Collection.
  • varStatus:  Name of the exported scoped variable for the status of the iteration
<table>
<c:forEach var="movie" items="${movieList}" varStatus="movieLoopCount" >
  <tr>
    <td>Count: ${movieLoopCount.count}</td>
  </tr>
  <tr>
    <td>${movie} <br><br></td>
  </tr>
  </c:forEach>
</table>
${movie} // movie is out of scope here, like normal loops
 
// The following are some additional ways to use the forEach tag: 
The following are some additional ways to use the forEach tag:
  <c:forEach var="x" begin="5" end="10">
    <c:out value="${x}"/>
  </c:forEach>
 
  <c:forEach var="str" items="${stringArr}">
    <c:out value="${str}"/><br>
  </c:forEach>
 
  <c:forEach var="p" items="${someMap}" begin="1" end="5">
    <c:out value="${p.key}"/> = <c:out value="${p.value}"/><br>
  </c:forEach>
 
  <c:forEach var="tok" items="a,b,c">
    <c:out value="${tok}"/><br>
  </c:forEach>

<c:forTokens>

The <c:forTokens> tag has similar attributes as <c:forEach> except one additional attribute delims (don't forget the "s") which specifies characters to use as delimiters.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
 
<c:forTokens items="Zara,nuha,roshy" delims="," var="name">
   <c:out value="${name}"/><p>
</c:forTokens>

<c:if>

  • <c:if> does not have an else, so use <c:choose> with <c:otherwise>
  • Note the use of both single and double quotes
<c:if test="${userType eq 'member' }" >
    <jsp:include page="inputComments.jsp"/>
</c:if>

<c:import>

  • Unlike the name suggest, import actually does an include
  • Allows you to include a file that doesn't exist in the webapp

Remember:

  • <%@ include file="" %>
  • <jsp:include page="" />
  • <c:import url="" />
<c:import url="http://www.wickedlysmart.com/skyler/horse.html" />
 
<jsp:include page="Header.jsp">
    <jsp:param name="subTitle" value="We take the sting out of SOAP." />
</jsp:include>
 
<c:import url="Header.jsp" >
    <c:param name="subTitle" value="We take the sting out of SOAP." />
</c:import>
 
<!-- You can specify the context attribute if the resource is in another webapp -->
<c:import url="/test2.jsp" context='anotherwebapp' />
 
<!-- If you include the var attribute, the text is not included automatically. It is included only where you use the variable. For example: -->
<c:import url="/test2.jsp" var='text' scope='request' /> 
 
<c:out value='${text}' />

<c:out>

  • <c:out> escapes HTML by default, NOT rendering it. You can use the escapeXml attribute to be explicit
  • It is useful to help prevent cross-site scripting
  • nulls come out blank, like EL
<div class='tipBox'>
    <b>Tip of the Day:</b> <br/> <br/>
    <c:out value='${pageContent.currentTip}' escapeXml='true' /> // true is the default
</div>
 
Characters that are escaped:
< &lt;
> &gt;
& &amp;
' &#039;
" &#034;
 
// using a default value when nulls occur, two ways:
<b>Hello <c:out value='${user}' default='guest' />.</b>
<b>Hello <c:out value='${user}'>guest</c:out></b>

<c:redirect>

The <c:redirect> tag redirects the browser to an alternate URL by providing automatically URL rewriting, it supports context-relative URLs, and it supports the <c:param> tag.

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

<c:remove>

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

<c:set>

Similar to <jsp:setProperty>. Two variations:

  • var - setting attribute variables
  • target - only setting bean properties or Map values

Traps:

  • You can never have BOTH the "var" and "target" attributes in a <c:set>.
  • "Scope" is optional, but if you don't use it the default is page scope.

var:

  • If the "value" is null, the attribute named by "var" will be removed!
  • If the attribute named by "var" does not exist, it'll be created, but only if "value" is not null.

target:

  • If the "target" expression is null, the Container throws an exception.
  • The "target" is for putting in an expression that resolves to the Real Object. If you put in a String literal that represents the "id" name of the bean or Map, it won't work. In other words, "target" is not for the attribute name of the bean or Map—it's for the actual attribute object.
  • If the "target" expression is not a Map or a bean, the Container throws an exception.
  • If the "target" expression is a bean, but the bean does not have a property that matches "property", the Container throws an exception. Remember that the EL expression ${bean.notAProperty} will also throw an exception
<%-- var --%>
<c:set var="userLevel" scope="session" value="Cowboy" />
<c:set var="userLevel" scope="session" >
    Sheriff, Bartender, Cowgirl
</c:set>
 
<%-- target, only beans and maps --%>
<c:set target="${PetMap}" property="dogName" value="Clover" />
<c:set target="${person}" property="name" >
    ${foo.name}
</c:set>

<c:url>

  • automatically does URL-rewriting for sessions when cookies are disabled
  • does not automatically encode URLs. For that use <c:param>
<a href="<c:url value='/inputComments.jsp' />">Click here</a>
 
<!--URL re-writing and URL encoding -->
<c:url value="/inputComments.jsp" var="inputURL" >
    <c:param name="firstName" value="${first}" />
    <c:param name="lastName" value="${last}" />
</c:url>






Comments

If you have questions, corrections, information or examples to add, please comment below.

Add a New Comment
or Sign in as Wikidot user
(will not be published)
- +
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License