Writing Custom Tag and Function Libraries - Creating Enterprise Applications - PROFESSIONAL JAVA FOR WEB APPLICATIONS (2014)

PROFESSIONAL JAVA FOR WEB APPLICATIONS (2014)

Part I Creating Enterprise Applications

Chapter 8 Writing Custom Tag and Function Libraries

IN THIS CHAPTER

· All about TLDs, tag files, and tag handlers

· Creating an HTML template using a tag file

· How to create a more useful date formatting tag handler

· Abbreviating strings using an EL function

· How to replace Java code with custom JSP tags

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the wrox.com code downloads for this chapter at www.wrox.com/go/projavaforwebapps on the Download Code tab. The code for this chapter is divided into the following major examples:

· c.tld

· fn.tld

· Template-Tags Project

· Customer-Support-v6 Project

NEW MAVEN DEPENDENCIES FOR THIS CHAPTER

In addition to the Maven dependencies introduced in previous chapters, you also need the following Maven dependency:

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-lang3</artifactId>

<version>3.1</version>

<scope>compile</scope>

</dependency>

UNDERSTANDING TLDS, TAG FILES, AND TAG HANDLERS

In Chapter 7 you explored the Java Standard Tag Library (JSTL) and also got a brief introduction to the Tag Library Descriptor (TLD), a special file that describes the tags and/or functions in a library. In this chapter you learn more about the TLD and how tags and functions are declared within them. You also learn how to create tag handlers and tag files.

All JSP tags result in execution of some tag handler. The tag handler is an implementation of javax.servlet.jsp.tagext.Tag or javax.servlet.jsp.tagext.SimpleTag and contains the Java code necessary to achieve the tag’s wanted behavior. A tag handler is specified within a tag definition in a TLD, and the container uses this information to map a tag in a JSP to the Java code that should execute in place of that tag.

However, tags do not always have to be written as Java classes explicitly. Just like the container can translate and compile JSPs into HttpServlets, it can also translate and compile tag files into SimpleTags. Tag files are not as powerful as straight Java code, and you cannot do things like parse nested tags within a tag file like you can with an explicit tag handler, but tag files do have the advantages of using simple markup like JSPs and allowing the use of other JSP tags within them. A tag definition in a TLD can point to either a tag handler class or to a tag file. However, you do not have to create a TLD to use a tag defined in a tag file. The taglib directive enables you to do this using the tagdir attribute:

<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>

Notice how this taglib directive is different from others you have seen. Instead of specifying a URI for a TLD file containing the tag library’s definitions, it specifies a directory in which tag files can be found. Any .tag or .tagx files within the tagdir directory are bound to the myTags namespace in this case. Tag files in an application must be within the /WEB-INF/tags directory, but they may also be within a subdirectory of this directory. You could use this to have multiple name spaces of tag files in your application, as in the following example.

<%@ taglib prefix="t" tagdir="/WEB-INF/tags/template" %>

<%@ taglib prefix="f" tagdir="/WEB-INF/tags/formats" %>

NOTE The difference between .tag and .tagx is the same as the difference between .jsp and .jspx. The .tag files contain JSP syntax while .tagx files contain JSP Document (XML) syntax. For more information on the difference between JSP and JSP Document syntax, refer to “A Note About JSP Documents” in Chapter 4.

JSP tag files can also be defined in JAR files within your application’s /WEB-INF/lib directory, but the rules are slightly different. Whereas tag files in your application must be in /WEB-INF/tags and can be declared either in a TLD or with a taglib directive pointing to the directory, tag files in a JAR file are placed in the /META-INF/tags directory and must be declared within a TLD in the /META-INF directory (or subdirectory) of the same JAR file.

Reading the Java Standard Tag Library TLD

To write custom tag and function libraries, you must understand the JSP tag library XSD and how to write tags with it. The best way to demonstrate this is by example, and using something you are already familiar with should be helpful, so take a look at the TLD for the Core tag library from the JSTL. You can find this within the org.glassfish.web:javax.servlet.jsp.jstl Maven artifact JAR file (look for the /META-INF/c.tld file), or you can download it from the wrox.com code download site. Start by looking at the initial declaration within the file:

<?xml version="1.0" encoding="UTF-8" ?>

<!--...Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved...-->

<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

http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"

version="2.1">

...

</taglib>

If you are at all familiar with XML, you know that this just sets up the document root element and declares that the document is using the XML schema definition web-jsptaglibrary_2_1.xsd. This XML schema defines how a TLD is structured, just like web-app_3_1.xsdin web.xml defines how the deployment descriptor is structured. The only thing of importance about the JSP tag library XSD that is not obvious from reading through a TLD file is that the schema uses a strict element sequence, meaning that any elements you use must come in a certain order, or your TLD file will not validate. The first five elements within the document root declare general information about the tag library.

<description>JSTL 1.2 core library</description>

<display-name>JSTL core</display-name>

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

<short-name>c</short-name>

<uri>http://java.sun.com/jsp/jstl/core</uri>

For this code:

· The <description> and <display-name> elements provide useful names that XML tools (such as your IDE) can display, but are irrelevant to the actual content of the TLD and are completely optional. You can actually have as many <description>s and <display-name>s as you want (as long as order is maintained), allowing you to specify different display names and descriptions for different languages. Another optional element not shown here is <icon>, which must always come after <display-name> and before <tlib-version>. Don’t worry about <icon>; you will never need to use it.

· <tlib-version> is a required element. Any TLDs you create must have exactly one of these. It defines the version of your tag library and must contain only numbers and periods (groups of numbers that can be separated only with a single period).

· <short-name> indicates the preferred and default prefix (or namespace) for this tag library and is also required. It cannot contain white space or start with a number or underscore.

· The fifth element shown here, <uri>, defines the URI for this tag library. This element is not required, and (as explained in Chapter 7) the lack of a URI means that the <jsp-config> in the deployment descriptor must contain a <taglib> declaration for this tag library to be useable. It’s best to always use <uri> despite the fact that it’s optional, and remember that it doesn’t have to be (and really shouldn’t be) a URL to an actual resource. Your TLD can have only one <uri>.

Defining Validators and Listeners

The next element in the Core TLD defines a validator.

<validator>

<description>

Provides core validation features for JSTL tags.

</description>

<validator-class>

org.apache.taglibs.standard.tlv.JstlCoreTLV

</validator-class>

</validator>

Validators extend the javax.servlet.jsp.tagext.TagLibraryValidator class to validate a JSP at compile time to ensure it uses the library correctly. The validator in this case performs checks that, for example, make sure the <c:param> tag is nested only within tags that support it (such as <c:url> and <c:import>). Validator elements have nested, in order, zero or more <description> elements, exactly one required <validator-class> element, and zero or more <init-param> elements (which work like you’re accustomed to in the deployment descriptor). A tag library can have zero or more validators. Validators are difficult to implement because doing so requires you to actually parse the JSP syntax. Because validators are almost never needed and extremely complicated, they are not covered in this book.

Validators are mentioned here only because they must be declared after the <uri> element and before any <listener> elements. Listener declarations are identical to their counterparts in the deployment descriptor, and any valid Java EE listener class (ServletContextListener, HttpSessionListener, and others) can be declared here. However, it is extremely unusual for listeners to be declared within a TLD, and you do not see examples of that in this book. You may declare zero or more listeners in a TLD, and immediately following that you may declare zero or more tags, which are the next things you see in the Core TLD.

Defining Tags

The <tag> element is the workhorse of the TLD and is responsible for defining tags in your tag library.

<tag>

<description>

Catches any Throwable that occurs in its body and optionally

exposes it.

</description>

<name>catch</name>

<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>

<body-content>JSP</body-content>

<attribute>

<description>

Name of the exported scoped variable for the

exception thrown from a nested action. The type of the

scoped variable is the type of the exception thrown.

</description>

<name>var</name>

<required>false</required>

<rtexprvalue>false</rtexprvalue>

</attribute>

</tag>

It may have zero or more nested <description>, <display-name>, and <icon> elements, just like <taglib>. Generally, only <description> is used here. After these comes the required <name> element, which specifies the name of the JSP tag. In this case, the full tag name is<c:catch>, where c is the <short-name> (prefix) for the tag library and catch is the <name> of the tag. A tag can obviously have only one name. The <tag-class> element comes next and indicates the tag handler class (Tag or SimpleTag) responsible for executing the tag. Not shown in this example is the optional <tei-class> element, which comes next and specifies an extension of javax.servlet.jsp.tagext.TagExtraInfo for this tag. TagExtraInfo classes can validate the attributes used in a tag at translation time to ensure that their uses are proper. You may see these occasionally, but not often. In the Core tag library only the <c:import> and <c:forEach> tags provide extra info classes.

Next, <body-content> specifies what type of content is allowed nested within the tag. Its valid values are:

· empty — This indicates that the tag may not contain any nested content and must be an empty tag.

· scriptless — Tags with this body content type may have template text, EL expressions, and JSP tags within them, but no scriptlets or expressions. (Declarations are never allowed within the nested body content of a tag.)

· JSP — his indicates that the tag’s nested content may be any content that is otherwise valid in the JSP (including scripting, if that is enabled in the JSP, but no declarations).

· tagdependent — This tells the container not to evaluate the nested content of the tag, but instead to let the tag evaluate the content itself. Usually this means the content is a different language, such as SQL, XML, or encoded data.

After <body-content> come zero or more <variable> elements, which is not shown in the previous example and, indeed, cannot be found in any of the JSTL TLDs. These elements provide information about variables defined as a result of using this tag. The <variable>element has the following subelements that provide additional information about defined variables:

· <description> — This is an optional description for the variable.

· <name-given> — The (required) name of the variable created by this tag. This should be a valid Java identifier.

· <name-from-attribute> — The (required) name of an attribute whose value determines what the name of the variable will be. Note that this element conflicts with <name-given>. You must specify both of these elements, but only one of them should ever have a value. The other should always be empty. The <name-from-attribute> element must always refer to the name of a tag attribute whose type is String, which does not allow runtime expressions in its value, and which is used to specify the name of the variable.

· <variable-class> — This optional element indicates the fully qualified Java class name of the defined variable’s type. If not provided, it is assumed to be String.

· <declare> — This Boolean element, which defaults to true, indicates whether the variable defined is a new variable that will require a declaration. If false, it means the variable is already defined elsewhere and is merely changing.

· <scope> — Indicates the scope in which this variable will be defined. The default value is NESTED, which means that the variable is only available to code and actions nested within the tag. The other valid values are AT_BEGIN, which indicates that the variable is in scope for code nested within the tag and code coming after the tag, and AT_END, which means that the variable is only in scope for code that comes after the tag and not for code nested within the tag.

NOTE What exactly is a variable in this case? Why doesn’t the JSTL use this element in its TLDs? Unfortunately, there’s a lot of incomplete or inaccurate information online about <variable>s in the TLD (even the XML schema document doesn’t make it clear), and as always the whole story can be found in the official JSP specification document. A variable, in this case, kind of refers to both an EL variable and a Java (scripting) variable. In your tag handler or tag file, you create or assign a value to an EL variable by calling pageContext.setAttribute, jspContext.setAttribute, or getJspContext().setAttribute (depending on what type of handler or file it is). The <variable> attribute in your TLD tells the container to expect this EL variableand to copy its value to a scripting variable. (Some IDEs also use variable definitions to assist the developer in using the tag.) Without a <variable>, users of your tag can still access the variables you define, using EL. With a <variable>, on the other hand, users of your tag can access the variables you define using EL or Java code. Because the use of scripting in JSPs is discouraged (see Chapter 4), developers rarely use <variable>, and you will not see examples of it in this book.

WARNING The TagExtraInfo class also provides a way to specify variables defined by a tag. However, that is an old method of achieving this and should not be used anymore. You should only use <variable> elements in your TLD for this purpose.

After the <variable>s for your tag, you can define zero or more <attribute>s, something demonstrated in the <c:catch> tag defined previously. Attributes, as the name implies, define the attributes that can be specified for this tag. There are several subelements of an<attribute> that specify the details of the attribute it defines.

· <description> — This optionally specifies a description for this attribute.

· <name> — This required element indicates the name of this attribute. The value must be a valid Java identifier.

· <required> — This is an optional Boolean value that indicates whether this attribute is required when using this tag. The default value is false.

· <rtexprvalue> — This optional Boolean element, which defaults to false, indicates whether the attribute allows runtime expressions (EL or scripting expressions) to specify the attribute value. If true, runtime expressions are permitted. If false, attribute values are considered static and runtime expressions result in a translation-time error.

· <type> — This optional element specifies the fully qualified Java class name of the attribute type. If not specified, the type is assumed to be an Object.

· <deferred-value> — The presence of this optional element indicates that the attribute value is a deferred EL value expression, and as a result the tag handler will be passed the attribute value as a javax.el.ValueExpression. Normally, the container evaluates an expression before binding its return value to the attribute value. With deferred value, the unevaluated ValueExpression is bound to the attribute, instead. The tag handler can then later evaluate that expression zero or more times as needed. By default, it is assumed that the value of this expression will be coerced into an Object, but a nested <type> element can specify a more precise type, which is especially helpful when using this attribute in an IDE.

· <deferred-method> — The presence of this optional element indicates that the attribute value is a deferred EL method expression, and as a result the tag handler will be passed the attribute value as a javax.el.MethodExpression. This is similar to <deferred-value>except for the expression type. By default it is assumed that the signature of the method in the expression is void method(), but you can use the nested <method-signature> element to specify a more precise signature (such as void execute(java.lang.String) or boolean test(java.lang.Object)) so that the expected return type and the number and type of parameters are correctly documented. The method name here isn’t actually important and the container ignores it.

· <fragment> — If this optional Boolean element is set to true, it makes the attribute type javax.servlet.jsp.tagext.JspFragment and tells the container not to evaluate the JSP content contained in the attribute value. The tag handler can then manually evaluate the fragment zero or more times. If omitted, the default is false. Using <fragment>, code that uses the tag can specify the value of the attribute using a nested <jsp:attribute> tag instead of an actual XML attribute, as in the following example. This is the case even if the body content is set to empty.

<myTags:doSomething>

<jsp:attribute name="someAttribute">

Any <b>content</b> <fmt:message key="including.jsp.tags" />.

</jsp:attribute>

</myTags:doSomething>

A related element not often seen, <dynamic-attributes>, follows any <attribute> tags. You can specify this Boolean element exactly once or you can omit it. The default value is false, and it indicates whether attributes not otherwise specified by <attribute> elements are still permitted. Where this is most often seen is in a JSP tag that ultimately outputs an HTML tag. The code for that tag could obtain the dynamic attributes and then copy them from the JSP tag to the HTML tag at run time. Spring Framework’s form tag library, which you explore in Part II of this book, uses dynamic attributes for this exact reason. Dynamic attributes always permit EL and scriptlet expressions as values. To use dynamic attributes your tag handler class must implementjavax.servlet.jsp.tagext.DynamicAttributes.

Following <dynamic-attributes> is the optional <example> element (of which there can only be one). This is related to <description> and contains simple text providing examples of using the tag. Finally, your tag can have zero or more <tag-extension>s. This tag provides additional information about a tag useful for consumption by some tool, such as an IDE or validator. Tag extensions never affect the behavior of the tag or the container. They are abstract and do not contain any subelements; instead, the developer is responsible for defining a schema for the tag extensions he wants to implement. Tag extensions are unusual and complicated and are not covered in this book.

Defining Tag Files

You have already seen how you can use the taglib directive to collect a directory of tag files into a namespace of custom tags, and you now know that tag files are essentially JSPs with slightly different semantics. You learn the details of these semantics later in this section. However, you should also know how to define tag files within a Tag Library Descriptor. Remember that tag files shipped inside of JAR libraries must be defined inside a TLD. Also, if you have one or more tag files that you would like to group with one or more tag handlers or JSP functions into the same namespace, you need to define those tag files within the TLD even if they are not shipped inside of JAR libraries.

After all the <tag> elements in your TLD, you can place zero or more <tag-file> elements to define tag files that belong to your library. Within the <tag-file> element are the optional <description>, <display-name>, and <icon> elements that you should be accustomed to by now. Typically, only the <description> is specified. The <name> element is analogous to <tag>’s <name> element and specifies what the name of the tag following the prefix is. The next element is <path>, which specifies the path to the .tag file implementing this custom tag. The value of <path> must start with /WEB-INF/tags in web applications and /META-INF/tags in JAR files. The final two elements are <example> and <tag-extension>, which serve the same purpose as their counterparts within the <tag> element. You will not find an example of <tag-file> within the JSTL TLDs, but the following XML demonstrates its basic use.

<tag-file>

<description>This tag outputs bar.</description>

<name>foo</name>

<path>/WEB-INF/tags/foo.tag</path>

</tag-file>

Defining Functions

After defining tag files in your TLD, you may define zero or more JSP functions using the <function> element. This is not demonstrated in c.tld, but you can view the JSTL functions defined within fn.tld, which you can also find in /META-INF/ in the Maven artifact or download from the wrox.com download site. If you open this file, you can see the familiar header with the tag library description, display name, version, short name, and URI. The next thing you see is the first function:

<function>

<description>

Tests if an input string contains the specified substring.

</description>

<name>contains</name>

<function-class>

org.apache.taglibs.standard.functions.Functions

</function-class>

<function-signature>

boolean contains(java.lang.String, java.lang.String)

</function-signature>

<example>

<c:if test="${fn:contains(name, searchString)}">

</example>

</function>

As you can see, defining a function in a TLD is extraordinarily simple. Bypassing the <description>, <display-name>, <icon>, and <name> elements you are already familiar with for <tag> and <tag-file>, the important elements in this example are <function-class> and<function-signature>. The function class is just the fully qualified name of a standard Java class, and the function signature is literally the signature of a static method on that class. Any public static method on any public class can become a JSP function in this manner. The last two elements that you may use within <function> are <example> and <function-extension>, which are analogous to the <example> and <tag-extension> elements within tag and tag file definitions.

WARNING The <example> for the fn:contains function contains characters reserved in the XML language. This is bad practice, and very strict XML validators may flag this as an issue. You should always place content like this within a CDATA block (<![CDATA[ special content goes here ]]>).

Defining Tag Library Extensions

After all <tag>s, <tag-file>s, and <function>s in your TLD, you may define zero or more tag library extensions using the <taglib-extension> element. Tag library extensions, like tag extensions and function extensions, do not affect tag or container behavior and simply exist to support tooling. Their concept is abstract and there are no predefined subelements within <taglib-extension>; instead, the developer is expected to know what he wants to use the extension for and define the schema himself. This author has never seen a tag extension, function extension, or tag library extension in practice, and so they are not covered in this book.

Comparing JSP Directives and Tag File Directives

As discussed earlier in this chapter, tag files work essentially like JSP files do. They contain the same syntax and must follow the same basic rules, and at run time they get translated and compiled into Java just like JSPs do. Tag files can use any normal template text (including HTML), any other JSP tag, declarations, scriptlets, expressions, and expression language. It should not be surprising, however, that there are some minor differences between the two file formats, mainly concerning the directives available for tag files. In Chapter 4 you learned about the page, include, and taglib directives and how to use them in JSPs. Tag files can also use the include and taglib directives to include files and other tag libraries in the JSP, but there is no page directive in tag files. The include directive can be used to include .jsp, .jspf, and other .tag files in a .tag file, or .jspx and other .tagx files in a .tagx file. Using a taglib directive in a tag file is identical to using one in a JSP file.

Instead of the page directive, tag files have a tag directive. This directive replaces the necessary functionality from JSP’s page directive and also replaces many of the configuration elements from a <tag> element in a TLD file. The tag directive has the following attributes, none of which are required:

· pageEncoding — This is equivalent to the page directive pageEncoding attribute and sets the character encoding of the tag’s output.

· isELIgnored — Equivalent to its counterpart in the page directive, this instructs the container not to evaluate EL expressions in the tag file and defaults to false.

· language — This specifies the scripting language used in the tag file (currently only Java is supported), just like the language attribute in the page directive.

· deferredSyntaxAllowedAsLiteral — Just like the page directive attribute, this tells the container to ignore and not parse deferred EL syntax within the tag file.

· trimDirectiveWhitespaces — This tells the container to trim white space around directives, equivalent to the same attribute on the page directive.

· import — This attribute works just like the page directive’s import attribute. You can specify one or more comma-separated Java classes to import in this attribute, and you can use the attribute multiple times in the same tag directive or across multiple tagdirectives.

· description — This is the equivalent of the <description> element in a TLD file, and specifying it can be helpful for developers to understand your tag better.

· display-name — Equivalent to the <display-name> element in a TLD, there is usually no need to specify this.

· small-icon and large-icon — These attributes essentially replace the <icon> element in a TLD, and you should never need to specify them.

· body-content — This is the replacement for <body-content> in a TLD, with one minor change: Its valid values are empty, scriptless, and tagdependent. The JSP value available in a TLD is not valid for the body content of a tag specified in a tag file. Due to the limitations of how tag files work, you cannot use scriptlets or expressions within the nested body content when using a tag that was defined in a tag file. scriptless is the default value for this attribute.

· dynamic-attributes — This string attribute is the counterpart of the <dynamic-attributes> element in a TLD and indicates whether dynamic attributes are enabled. By default the value is blank, which means that dynamic attributes are not supported. To enable dynamic attributes, set its value to the name of the EL variable you want created to hold all of the dynamic attributes. The EL variable will have a type of Map<String, String>. The map keys will be dynamic attribute names, and the values attribute values.

· example — You can use this attribute to indicate example tag usage just like with the <example> element in a TLD, but it is very difficult to effectively do this in a directive attribute.

Notice that equivalent directive attributes are missing for the <name>, <tag-class>, <tei-class>, <variable>, <attribute>, and <tag-extension> elements. The tag name is inferred from and always equal to the tag filename (minus the .tag extension), and <tag-class> is not needed because the tag file is the tag handler (or will be, after the container compiles it). There is no way to specify a TagExtraInfo class or a tag extension for tag files because there is simply no equivalent. This leaves <variable> and <attribute>, which are replaced with the variable and attribute directives, respectively.

The variable directive provides description, name-given, name-from-attribute, variable-class, declare, and scope attributes that are equivalent to their identically named elements in a TLD. It also provides an additional attribute, alias, which enables you to specify a local variable name that you can use to reference the variable within your tag file. The attribute directive has description, name, required, rtexprvalue, type, fragment, deferredValue, deferredValueType, deferredMethod, and deferredMethodSignature attributes that correspond to elements in a TLD.

CREATING YOUR FIRST TAG FILE TO SERVE AS AN HTML TEMPLATE

Now that you are familiar with the details of Tag Library Descriptors and tag files, it’s time to create your first custom JSP tag. The simplest way to create a custom tag is by writing a tag file and using a taglib directive with the tagdir attribute. You need to follow along in the Template-Tags project on the wrox.com code download site because some of the code in the next several sections is too long to print in this book. Note the standard deployment descriptor has changed slightly since Chapter 7: <scripting-invalid>false</scripting-invalid> has changed to <scripting-invalid>true</scripting-invalid> to disable Java in JSPs. The project also has an index.jsp file in the web root that redirects to /index with <c:url>, and a /WEB-INF/jsp/base.jspf file with the following tag library declarations:

<%@ 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="template" tagdir="/WEB-INF/tags/template" %>

Next, create a simple Servlet that can respond to requests to /index. Right now it may look like overkill for such a simple action, but in later sections you will add some more logic to this.

@WebServlet(

name = "indexServlet",

urlPatterns = "/index"

)

public class IndexServlet extends HttpServlet

{

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException

{

String view = "hello";

request.getRequestDispatcher("/WEB-INF/jsp/view/" + view + ".jsp")

.forward(request, response);

}

}

One of the most powerful things you can do with tag files is establish a system of HTML templates for your application to use. This templating system can take care of many of the repetitive tasks needed on pages across your application, cutting down on duplicated code and making it easier to change the design of your site. To demonstrate this, create a /WEB-INF/tags/template/main.tag file containing the basic JSP layout that most pages in your application use.

<%@ tag body-content="scriptless" dynamic-attributes="dynamicAttributes"

trimDirectiveWhitespaces="true" %>

<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ include file="/WEB-INF/jsp/base.jspf" %>

<!DOCTYPE html>

<html<c:forEach items="${dynamicAttributes}" var="a">

<c:out value=' ${a.key}="${fn:escapeXml(a.value)}"' escapeXml="false" />

</c:forEach>>

<head>

<title><c:out value="${fn:trim(htmlTitle)}" /></title>

</head>

<body>

<jsp:doBody />

</body>

</html>

Now examine the previous code example:

· The first directive in this file establishes that uses of the tag can contain body content, that it supports dynamic attributes, that dynamic attributes are accessible with the dynamicAttributes EL variable, and that directive white space should be trimmed. Note the use of the trimDirectiveWhitespace attribute to accomplish this; the <jsp-config> in the deployment descriptor does not affect tag files, so you need to do this manually in each tag file.

· The second directive establishes the explicit htmlTitle attribute.

· The third attribute includes the base.jspf file, again because <jsp-config> does not affect tag files.

· The <c:forEach> loop copies all the dynamic attributes into the <html> tag. This isn’t something you would normally need to do, but it does demonstrate how useful dynamic attributes can be.

· The <c:out> tag in the loop may seem unnecessary, but it is done this way for a reason: This ensures that the space between each attribute is not ignored, that the attribute values are escaped properly, and that the quotes around the attribute values are not escaped.

· The htmlTitle attribute is output as the document <title> using <c:out> and the EL expression trimming its value.

· The special <jsp:doBody> tag is used within the HTML <body>. This tag, which can be used only within tag files, tells the container to evaluate the content of the JSP tag call and place it inline. You could also specify the var or varReader attributes and the scope attribute to output the evaluated body content to a variable instead of inlining it.

Now to put all this to use, create the /WEB-INF/jsp/view/hello.jsp file that calls the <template:main> tag you have created.

<template:main htmlTitle="Template Homepage">

Hello, Template!

</template:main>

That’s all there is to it! Compile the application and start your debugger; then navigate to http://localhost:8080/template-tags/index. If you view the response source for the resulting page, you see that the document title is “Template Homepage” and that the text “Hello, Template!” was placed within the document body. You have successfully created your first custom JSP tag! In the next section you demonstrate the power of this HTML template more fully when you create another page to show off your next custom tag.

CREATING A MORE USEFUL DATE FORMATTING TAG HANDLER

In the previous chapter, you explored the JSTL and learned about internationalization, localization, and the standard fmt tag library. You may recall that you were promised you would create a replacement for the <fmt:formatDate> tag, which was so limited in its capabilities. Some of the major drawbacks you may have noticed with this tag were its inability to format anything other than a java.util.Date, place the time before the date when needed, or place a token string between the date and time. The first drawback required you to always convert all date and time instances to a Date, and the second and third required you to either use two <fmt:formatDate> tags or use the pattern attribute, neither of which is ideal.

To create a better formatting tag, start by thinking about the date types you would like to support. Obviously, you would want to support Date and probably also Calendar. Then there’s the Java 8 Date and Time API, which has many different types representing dates and times. Thankfully, the API has a common interface, java.time.temporal.TemporalAccessor, that all the major types representing dates and times implement. Add some code to the doGet method of IndexServlet to put dates of each type on the page model.

if(request.getParameter("dates") != null)

{

request.setAttribute("date", new Date());

request.setAttribute("calendar", Calendar.getInstance());

request.setAttribute("instant", Instant.now());

view = "dates";

}

Now that you know what you need your tag to do, think about how you want to use it. Create a TLD file that specifies the behavior and attributes for the new date formatting tag. Doing this before actually writing the tag handler is an way of writing an interface based on use cases and then coding to the interface. The tag definition in your TLD is the interface in this case. Listing 8-1 declares a wrox tag library with the URI http://www.wrox.com/jsp/tld/wrox and specifies a single tag conveniently named formatDate.

The entire tag declaration is more than 100 lines long—too long to print in this book. You need to download the Template-Tags project from the wrox.com code download site to see the whole thing. The tag has many of the same attributes as <fmt:formatDate>, and as you read the TLD, you can understand the purpose for each attribute through its description.

LISTING 8-1: wrox.tld

<?xml version="1.0" encoding="ISO-8859-1"?>

<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

http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"

version="2.1">

<tlib-version>1.0</tlib-version>

<short-name>wrox</short-name>

<uri>http://www.wrox.com/jsp/tld/wrox</uri>

<tag>

<description><![CDATA[...]]></description>

<name>formatDate</name>

<tag-class>com.wrox.tag.FormatDateTag</tag-class>

<body-content>empty</body-content>

<attribute>

<description>...</description>

<name>value</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

<attribute>

...

</attribute>

...

<dynamic-attributes>false</dynamic-attributes>

</tag>

</taglib>

Implementing this tag is not a simple task. The FormatDateTag class, which extends TagSupport and utilizes Java 8 lambda expressions and method references to take care of some of the harder work, contains several hundred lines of code, and is too long to print in this book. You can find the implementation of this class in the Template-Tags project on the wrox.com code download site. There are many important things to note about this class.

First, the static fields are used to make the reflective access to the Apache/GlassFish JSTL implementation more efficient. The Apache classes have already implemented the hard work of resolving the locale and time zone information according to the JSTL specification, which consists of several hundred lines of code that it would not be ideal to duplicate. Of course, using these classes requires you to stick to that specific JSTL implementation, which isn’t always ideal either. In a true enterprise application, you would probably want to copy the relevant Open Source code into your application so that it was more portable. For demonstration purposes, using the Apache classes is adequate. The private getLocale and getTimeZone methods use these static fields to invoke the protected methods in the Apache classes.

The init method, which is called from the constructor and the release method, resets all the tag attributes to their default values. This is important because containers pool and reuse tag handlers to improve efficiency, and between each use of the tag they call therelease method to reset it before setting all the attributes again. A series of mutator methods (“setters”) set all of the values for the tag attributes. The org.apache.commons.lang3.StringUtils class from Apache Commons Lang makes testing the string attributes for nullor blank easier in these methods. The setTimeZone method enables a String, java.util.TimeZone, java.time.ZoneId, or null value and throws an exception if some other type is passed in.

The doEndTag method is the method that the container invokes when it is ready to close the tag. For tags that permit body content, doStartTag and doAfterBody could also be overridden to execute code at different times, but because this tag doesn’t allow body content, only doEndTag needs be overridden. The doEndTag outputs nothing and removes the scope variable (if applicable) if the date value is null. It then invokes one of two formatDate methods based on the value type and either outputs the formatted date or sets it to the specified scope variable. The two formatDate methods set up the appropriate formatters for the differing date types and then pass those formatters to the third formatDate method, which executes them in the proper order depending on the timeFirst andseparateDateTimeWith attribute values. Before Java 8, this would require DateFormat and DateTimeFormatter to inherit from a common interface, but they do not. Using Java 8 lambdas, references to the format methods of each formatter are passed instead and invoked when necessary.

To use the newly created tag, add a taglib directive to /WEB-INF/jsp/base.jspf:

<%@ taglib prefix="wrox" uri="http://www.wrox.com/jsp/tld/wrox" %>

Now create the /WEB-INF/jsp/view/dates.jsp view to format the dates previously created in the Servlet.

<%--@elvariable id="date" type="java.util.Date"--%>

<%--@elvariable id="calendar" type="java.util.Calendar"--%>

<%--@elvariable id="instant" type="java.time.Instant"--%>

<template:main htmlTitle="Displaying Dates Properly">

<b>Date:</b>

<wrox:formatDate value="${date}" type="both" dateStyle="full"

timeStyle="full" /><br />

<b>Date, time first with separator:</b>

<wrox:formatDate value="${date}" type="both" dateStyle="full"

timeStyle="full" timeFirst="true"

separateDateTimeWith=" on " /><br />

<b>Calendar:</b>

<wrox:formatDate value="${calendar}" type="both" dateStyle="full"

timeStyle="full" /><br />

<b>Instant:</b>

<wrox:formatDate value="${instant}" type="both" dateStyle="full"

timeStyle="full" /><br />

<b>Instant, time first with separator:</b>

<wrox:formatDate value="${instant}" type="both" dateStyle="full"

timeStyle="full" timeFirst="true"

separateDateTimeWith=" on " /><br />

</template:main>

Compile and debug your application and go to http://localhost:8080/template-tags/index?dates in your browser. You should see all three dates formatted, two of them twice each with different formats, just as specified. This new tag you have created is much more flexible and powerful than the stock <fmt:formatDate> tag that comes with the JSTL.

UNDERSTANDING THE DIFFERENT TYPES OF TAG HANDLERS

You can write tag handlers in different ways to achieve many different tasks, and this book simply cannot cover all the possibilities. But it’s important to understand the general differences between these different tag types. All tags must implement either the Tag interface or SimpleTag interface, both of which extend the JspTag marker interface. Tag is the classic tag handler that has been around since the earliest days of JSP tags, whereas SimpleTag was added in JSP 2.0 to make tags — well — simpler to write.

SimpleTag may be simpler to use for many tasks, but among these advantages come a key disadvantage: SimpleTag classes are instantiated, used exactly once, and then thrown away. Tag instances can be pooled and reused, which can net significant performance gains on heavily used tags or tags that carry around many resources. However, you can’t ignore how much easier it is to implement a loop, for example, in a SimpleTag. (The invoke method in this case is writing the invoked body to the JSP output, but passing in a Writer instead of null causes the body to be written to that Writer.)

public void doTag() throws JspException, IOException

{

while(condition-is-true)

{

this.getJspContext().setAttribute("someElVariable", value);

this.getJspBody().invoke(null);

}

}

Compare this to how complex it is to implement the same loop in a Tag (implementing IterationTag, which extends Tag):

public int doStartTag() throws JspException

{ // this is invoked exactly once

if(condition-is-true)

{

this.pageContext.setAttribute("someElVariable", value);

return Tag.EVAL_BODY_INCLUDE;

}

return Tag.SKIP_BODY;

}

public int doAfterBody() throws JspException

{ // this is invoked as many times as needed

if(condition-is-true)

{

this.pageContext.setAttribute("someElVariable", value);

return Tag.EVAL_BODY_AGAIN;

}

return Tag.SKIP_BODY;

}

public int doEndTag() throws JspException

{ // this is invoked exactly once

return Tag.EVAL_PAGE;

}

You must evaluate each tag you need to create to determine whether it necessitates prioritizing performance over convenience. Keep in mind that doStartTag may return only Tag.SKIP_BODY or Tag.EVAL_BODY_INCLUDE; doAfterBody may return onlyTag.SKIP_BODY or IterationTag.EVAL_BODY_AGAIN; and doEndTag may return only Tag.SKIP_PAGE or Tag.EVAL_PAGE.

The following list indicates the various tag interfaces you can implement (and the helper classes you should actually extend) and when you would implement each.

· SimpleTag extends JspTag — Use this for all tags that do not require the performance gains achieved through pooling. You should normally extend SimpleTagSupport.

· Tag extends JspTag — Use this poolable tag handler for most simple tags that do not do things like loop or access their body content. (The body content can still be evaluated with this tag, just not accessed.) You should normally extend TagSupport.

· LoopTag extends Tag — This tag is not often used, as it is only useful for looping over collections (or arrays) of objects. You should normally extend LoopTagSupport.

· IterationTag extends Tag — This is a more useful iteration tag that can iterate based on any condition, including looping over collections of objects. You should normally extend IterationTagSupport.

· BodyTag extends IterationTag — This iteration tag enables the output of the evaluated body to be buffered into a BodyContent object that can later be used for some other purpose (like saving the output to a variable). This is achieved by returningBodyTag.EVAL_BODY_BUFFERED from doStartTag instead of Tag.EVAL_BODY_INCLUDE and then accessing the body content in doEndTag. You should normally extend BodyTagSupport.

CREATING AN EL FUNCTION TO ABBREVIATE STRINGS

Recall from earlier in the chapter that you can use a TLD to define EL functions as well as tags. Remember that EL function definitions are simple; all you need is a public static method of some class and you can define an EL function that maps to that. You don’t even have to write the static method yourself. It can be something from the Java SE library, the Java EE library, or a completely different third-party library. Consider StringUtils from the Apache Commons Lang library. It has a very handy method, abbreviate, that ensures a string does not exceed a certain length. If the string is too long, it gets shortened and an ellipsis (...) is added to the end of the string. This can be a very useful action in the world of web applications, in which user input can often be long and sometimes needs to be shortened for summary display or to keep the page layout from being thrown off.

To turn this useful method into an EL function that can easily be called from your JSPs, follow these steps:

1. Open the wrox.tld file and add the following <function> below the date formatting <tag>:

2. <function>

3. <description>...</description>

4. <name>abbreviateString</name>

5. <function-class>org.apache.commons.lang3.StringUtils</function-class>

6. <function-signature>

7. java.lang.String abbreviate(java.lang.String,int)

8. </function-signature>

</function>

It should be obvious how much easier it is to define an EL function than a JSP tag. The <function-class> element specifies the fully qualified class name that the method belongs to and the <function-signature> element specifies which method on the class makes up the function.

9. Now add a little bit more logic to the doGet method of your IndexServlet:

10. else if(request.getParameter("text") != null)

11. {

12. request.setAttribute("shortText", "This is short text.");

13. request.setAttribute(

14. "longText",

15. "This is really long text that should get cut

16. off at 32 chars."

17. );

18. view = "text";

}

19.Create a /WEB-INF/jsp/view/text.jsp view to demonstrate using the EL function.

20. <%--@elvariable id="shortText" type="java.lang.String"--%>

21. <%--@elvariable id="longText" type="java.lang.String"--%>

22. <template:main htmlTitle="Abbreviating Text">

23. <b>Short text:</b> ${wrox:abbreviateString(shortText, 32)}<br />

24. <b>Long text:</b> ${wrox:abbreviateString(longText, 32)}<br />

</template:main>

25.Compile and start up the Template-Tags project one more time and navigate to http://localhost:8080/template-tags/index?text in your browser.

You should see that your new EL function has abbreviated only the second string and has added an ellipsis to the end of that string.

REPLACING JAVA CODE WITH CUSTOM JSP TAGS

Previously you were promised that you would finally get to replace all Java code in the Customer Support application JSPs with JSP tags you created in this chapter. Indeed, you can do this with everything you have learned in this chapter. You can follow along in the Customer-Support-v6 project available on the wrox.com code download site, or you can make the changes noted here. You should start by changing <scripting-invalid>false</scripting-invalid> in your deployment descriptor to <scripting-invalid>true</scripting-invalid>. This change proves that you have replaced all Java code in your JSPs because any JSPs with Java code cannot compile with this setting enabled.

Now copy the com.wrox.tag.FormatDateTag class and /WEB-INF/tld/wrox.tld file from the Template-Tags project to the support project; you can use the date formatting tag and string abbreviating function in the support application. You can also add another function to the TLD. Create a TimeUtils class like the following:

public final class TimeUtils

{

public static String intervalToString(long timeInterval)

{

if(timeInterval < 1_000)

return "less than one second";

if(timeInterval < 60_000)

return (timeInterval / 1_000) + " seconds";

return "about " + (timeInterval / 60_000) + " minutes";

}

}

You can then add a function to the bottom of the TLD that calls the method in the TimeUtils class.

<function>

<description>

Formats a time interval in an attractive way, such as "less than one

second" or "ten seconds" or "about 12 minutes".

</description>

<name>timeIntervalToString</name>

<function-class>com.wrox.TimeUtils</function-class>

<function-signature>

java.lang.String intervalToString(long)

</function-signature>

</function>

Make sure the /WEB-INF/jsp/base.jspf file has the appropriate taglib declarations in it.

<%@ 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="wrox" uri="http://www.wrox.com/jsp/tld/wrox" %>

<%@ taglib prefix="template" tagdir="/WEB-INF/tags/template" %>

You’re almost ready to begin changing your JSPs. You’ll also create a few tag files to help you template the support application and avoid duplicating presentation code. Start with /WEB-INF/tags/template/main.tag in Listing 8-2, which is much more complex than the template tag from the Template-Tags project.

Notice that two of the attributes for this tag are simple strings, but the headContent and navigationContent attributes are JSP fragments. This allows those attributes to contain JSP content that you can later evaluate. In the proper place, the tag evaluates these fragments using the <jsp:invoke> tag. This is similar to <jsp:body> except that it acts on fragment attributes instead of the tag body content.

LISTING 8-2: main.tag

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>

<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ attribute name="headContent" fragment="true" required="false" %>

<%@ attribute name="navigationContent" fragment="true" required="true" %>

<%@ include file="/WEB-INF/jsp/base.jspf" %>

<!DOCTYPE html>

<html>

<head>

<title>Customer Support :: <c:out value="${fn:trim(htmlTitle)}" /></title>

<link rel="stylesheet"

href="<c:url value="/resource/stylesheet/main.css" />" />

<jsp:invoke fragment="headContent" />

</head>

<body>

<h1>Multinational Widget Corporation</h1>

<table border="0" id="bodyTable">

<tbody>

<tr>

<td class="sidebarCell">

<jsp:invoke fragment="navigationContent" />

</td>

<td class="contentCell">

<h2><c:out value="${fn:trim(bodyTitle)}" /></h2>

<jsp:doBody />

</td>

</tr>

</tbody>

</table>

</body>

</html>

You might wonder how you’re supposed to specify an attribute that contains JSP content; after all, such a value might be many lines long and contain other JSP tags. /WEB-INF/tags/template/loggedOut.tag in Listing 8-3 demonstrates this. When you use a JSP tag, you typically specify all the attributes as normal XML attributes and then the contents within the tag make up the entire tag body. However, when an attribute value is too long or contains JSP content, you can use the <jsp:attribute> tag within the tag body to specify the attribute value.

At this point the attribute is specified within the tag body content, so to specify the real tag body content you need to use the <jsp:body> tag. Everything in this tag becomes the tag body for the enclosing tag (<template:main> in this case). Listing 8-4, /WEB-INF/tags/template/basic.tag, demonstrates this again but with a lot more content in the fragment attributes. This tag specifies several links that go within the sidebar of the page on every page that uses this template tag. It also provides extraHeadContent andextraNavigationContent attributes to allow consuming pages to add extra content to the head tag or sidebar.

Although tag files cannot technically extend one another, this is essentially what you are doing. The main tag sets up a foundation for the template and the loggedOut and basic tags build upon that foundation. Now you can see the full power of tag files.

LISTING 8-3: loggedOut.tag

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>

<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ include file="/WEB-INF/jsp/base.jspf" %>

<template:main htmlTitle="${htmlTitle}" bodyTitle="${bodyTitle}">

<jsp:attribute name="headContent">

<link rel="stylesheet"

href="<c:url value="/resource/stylesheet/login.css" />" />

</jsp:attribute>

<jsp:attribute name="navigationContent" />

<jsp:body>

<jsp:doBody />

</jsp:body>

</template:main>

LISTING 8-4: basic.tag

<%@ tag body-content="scriptless" trimDirectiveWhitespaces="true" %>

<%@ attribute name="htmlTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ attribute name="bodyTitle" type="java.lang.String" rtexprvalue="true"

required="true" %>

<%@ attribute name="extraHeadContent" fragment="true" required="false" %>

<%@ attribute name="extraNavigationContent" fragment="true" required="false" %>

<%@ include file="/WEB-INF/jsp/base.jspf" %>

<template:main htmlTitle="${htmlTitle}" bodyTitle="${bodyTitle}">

<jsp:attribute name="headContent">

<jsp:invoke fragment="extraHeadContent" />

</jsp:attribute>

<jsp:attribute name="navigationContent">

<a href="<c:url value="/tickets" />">List Tickets</a><br />

<a href="<c:url value="/tickets">

<c:param name="action" value="create" />

</c:url>">Create a Ticket</a><br />

<a href="<c:url value="/sessions" />">List Sessions</a><br />

<a href="<c:url value="/login?logout" />">Log Out</a><br />

<jsp:invoke fragment="extraNavigationContent" />

</jsp:attribute>

<jsp:body>

<jsp:doBody />

</jsp:body>

</template:main>

NOTE You may have noticed two CSS files used in these tags, /resource/stylesheet/main.css and /resource/stylesheet/login.css. These style sheets make the application more attractive but are not required for the application to function properly, so they are not printed in this book. You can find them in the Customer-Support-v6 project on the wrox.com code download site.

Now you need just a few simple changes to the Java classes in the application. First, add a creation date field and appropriate mutator and accessor to the Ticket class:

private OffsetDateTime dateCreated;

In the TicketServlet class, add the following line to the createTicket method to assign a value to the creation date field:

ticket.setDateCreated(OffsetDateTime.now());

In the doGet method of SessionListServlet, add the following request attribute before the request dispatcher forwards the request:

request.setAttribute("timestamp", System.currentTimeMillis());

You are now ready to change the JSPs in /WEB-INF/jsp/view to use the new template and wrox tag libraries. login.jsp is simple and uses the <template:loggedOut> tag:

<%--@elvariable id="loginFailed" type="java.lang.Boolean"--%>

<template:loggedOut htmlTitle="Log In" bodyTitle="Log In">

You must log in to access the customer support site.<br /><br />

<c:if test="${loginFailed}">

<b>The username and password you entered are not correct. Please try

again.</b><br /><br />

</c:if>

<form method="POST" action="<c:url value="/login" />">

Username<br />

<input type="text" name="username" /><br /><br />

Password<br />

<input type="password" name="password" /><br /><br />

<input type="submit" value="Log In" />

</form>

</template:loggedOut>

All the other tickets use the <template:basic> tag. ticketForm.jsp hasn’t changed at all except for using the template:

<template:basic htmlTitle="Create a Ticket" bodyTitle="Create a Ticket">

<form method="POST" action="tickets" enctype="multipart/form-data">

<input type="hidden" name="action" value="create"/>

Subject<br />

<input type="text" name="subject"><br /><br />

Body<br />

<textarea name="body" rows="5" cols="30"></textarea><br /><br />

<b>Attachments</b><br />

<input type="file" name="file1"/><br /><br />

<input type="submit" value="Submit"/>

</form>

</template:basic>

In addition to using the template, viewTicket.jsp now uses the <wrox:formatDate> tag to display the date the ticket was created:

<%--@elvariable id="ticketId" type="java.lang.String"--%>

<%--@elvariable id="ticket" type="com.wrox.Ticket"--%>

<template:basic htmlTitle="${ticket.subject}"

bodyTitle="Ticket #${ticketId}: ${ticket.subject}">

<i>Customer Name - <c:out value="${ticket.customerName}" /><br />

Created <wrox:formatDate value="${ticket.dateCreated}" type="both"

timeStyle="long" dateStyle="full" /></i><br /><br />

<c:out value="${ticket.body}" /><br /><br />

<c:if test="${ticket.numberOfAttachments > 0}">

Attachments:

<c:forEach items="${ticket.attachments}" var="attachment"

varStatus="status">

<c:if test="${!status.first}">, </c:if>

<a href="<c:url value="/tickets">

<c:param name="action" value="download" />

<c:param name="ticketId" value="${ticketId}" />

<c:param name="attachment" value="${attachment.name}" />

</c:url>"><c:out value="${attachment.name}" /></a>

</c:forEach><br /><br />

</c:if>

</template:basic>

listTickets.jsp uses not only the date formatter, but also the wrox:abbreviateString EL function to truncate ticket subjects to 60 characters:

<%--@elvariable id="ticketDatabase"

type="java.util.Map<Integer, com.wrox.Ticket>"--%>

<template:basic htmlTitle="Tickets" bodyTitle="Tickets">

<c:choose>

<c:when test="${fn:length(ticketDatabase) == 0}">

<i>There are no tickets in the system.</i>

</c:when>

<c:otherwise>

<c:forEach items="${ticketDatabase}" var="entry">

Ticket ${entry.key}: <a href="<c:url value="/tickets">

<c:param name="action" value="view" />

<c:param name="ticketId" value="${entry.key}" />

</c:url>">

<c:out value="${wrox:abbreviateString(entry.value.subject, 60)}" />

</a><br />

<c:out value="${entry.value.customerName}" /> created ticket

<wrox:formatDate value="${entry.value.dateCreated}" type="both"

timeStyle="short" dateStyle="medium" /><br />

<br />

</c:forEach>

</c:otherwise>

</c:choose>

</template:basic>

Notice that sessions.jsp experienced the largest change. All the Java code for declaring the method, looping over the sessions, and outputting data is gone, replaced by 100 percent JSP code using <c:forEach>, <c:out>, <c:if>, and wrox:timeIntervalToString.

<%--@elvariable id="timestamp" type="long"--%>

<%--@elvariable id="numberOfSessions" type="int"--%>

<%--@elvariable id="sessionList"

type="java.util.List<javax.servlet.http.HttpSession>"--%>

<template:basic htmlTitle="Active Sessions" bodyTitle="Active Sessions">

There are a total of ${numberOfSessions} active sessions in this

application.<br /><br />

<c:forEach items="${sessionList}" var="s">

<c:out value="${s.id} - ${s.getAttribute('username')}" />

<c:if test="${s.id == pageContext.session.id}"> (you)</c:if>

- last active

${wrox:timeIntervalToString(timestamp - s.lastAccessedTime)} ago<br />

</c:forEach>

</template:basic>

You can now compile and run your application and go to http://localhost:8080/support/login in your browser. Log in to the support application to create, view, and list tickets. Notice the formatted dates on the view and list pages, and the convenient sidebar on the left side of the page. Create a ticket with a long subject and see how it gets chopped off before wrapping on the list page. You now have all the tools you need to create useful, dynamic JSPs without any Java code embedded in them.

SUMMARY

In this chapter you learned about creating custom JSP tags and EL functions. You explored the Tag Library Descriptor (TLD) by taking a look at TLDs from the Java Standard Tag Library (JSTL) and by creating your own TLD. You examined the concept of tag files and used this technology to create powerful HTML templates to serve as a base for your application’s pages. You created a better date and time formatting tag that provides more flexible formatting options and supports Date, Calendar, and the Java 8 Date and Time API, while maintaining the locale and time zone contract established in the JSTL. At this point you know everything you could need to know to create Java-free JSPs.

From here on you switch gears and look at more advanced technologies. In the next chapter you explore filters and how you can usefully apply them to your applications.