J2EE & Web Services

Creating JSP 2.0 Tag Files
by Andrei Cioroianu

Learn how to build reusable Web templates and database scripts using JSP, JSTL, and SQL

Download the source code for this article

Tag files are one of the most important new additions to JavaServer Pages (JSP) technology, allowing Web developers to build libraries of custom tags with the JSP syntax. The JSP tag files are translated into Java code automatically by the JSP container through the same process that transparently produces Java Servlets from JSP pages. We could say that tag files hide the complexity of building custom JSP tag libraries. While such libraries can be reused across Web applications, they offer significant benefits even when designed for a specific application because custom tags improve the maintainability of the Web pages. After reading this article, you'll know how to create and use tag files and how to transform existing page fragments into tag files.

JSP 2.0 has defined three new directives (<%@tag%>, <%@attribute%>, and <%@variable%>) and two new standard actions (<jsp:invoke> and <jsp:doBody>) that can be used only in tag files. We'll use them throughout the article, and you'll find out how to take advantage of the more-advanced tag file features such as "dynamic attributes," "fragment attributes," and "variables named from attributes." Tag files can be a powerful tool when used together with the JSP Standard Tag Library (JSTL), which introduced the expression language adopted by JSP 2.0 and many JSP actions, including a set of SQL tags. We'll use JSTL and the advanced JSP features to build tag files that update and query a database.

Tag Files Overview

JSP 1.x allows Web developers to build Java components (called tag handlers) that are invoked from JSP pages via custom tags. Tag handlers are like the old Java Servlets because you use lots of println() calls to generate HTML content and then you have to compile your Java code. The tag files of JSP 2.0 are like the JSP pages, because you use the JSP syntax and then the JSP container takes your JSP tag files, parses them, generates Java tag handlers, and compiles them automatically. Tag files are invoked from JSP pages using custom tags matching the <prefix:tagFileName> pattern.

In order to be recognized by the JSP container, tag files must be named using the .tag file extension and they must be placed in the /WEB-INF/tags directory of your Web application or in a subdirectory of /WEB-INF/tags. If you adopt this deployment method, you don't have to create any Tag Library Descriptor (TLD) as you in the case of the JSP libraries implemented with Java tag handlers. You may also place your tag files in the /META-INF/tags directory of a .jar file, which can be deployed easier, but in this case you have to create a TLD and you have to repack your tag files after every change.

Tag files and JSP pages use almost the same syntax. The first difference you'll notice is the new <%@tag%> directive, which is the equivalent of <%@page%>. These two directives have similar attributes, but the former was designed for tag files while the latter can be used only in JSP pages. Instead of declaring their attributes and variables in a separate .tld file, tag files use the <%@attribute%> and <%@variable%> directives. When a tag file is invoked from a JSP page, the custom tag may have a body (between <prefix:tagFileName> and </prefix:tagFileName>) that can be executed by the tag file with the <jsp:doBody> action. You'll see how this works in the next section.

Using Tag Files Instead of Page Fragments

In order to have maintainable Web pages, many well-designed JSP 1.x applications use page fragments that are included with <jsp:include> or <%@include%> in larger pages, which isn't the ideal method for reusing JSP fragments in Web applications. Unlike the page-inclusion solution, tag files have been specifically designed for building libraries of reusable page fragments. This section compares the syntax of the old-fashioned JSP 1.x method with the much better syntax of the JSP 2.0 tag files.

Transforming included pages into tag files. Let's say you have a page named container.jsp that includes another page named part.jsp with <jsp:include page="part.jsp"/>. Execute the following steps to transform part.jsp into a tag file.

Step 1: Move the included page.
In the WEB-INF directory of your application, create a subdirectory named tags and move the part.jsp page into WEB-INF/tags.

Step 2: Rename the included page.
Because the standard tag file extension is .tag, you have to rename part.jsp to part.tag.

Step 3: Edit the tag file.
Open the part.tag file for editing and replace the <%@page%> directive with a similar <%@tag%> directive. If you get any compilation errors, some other minor changes might be necessary. Use jspContext instead of pageContext.

Step 4: Declare the tag library.
Insert <%@taglib prefix="tags" tagdir="/technology/WEB-INF/tags"%> at the beginning of container.jsp.

Step 5: Invoke the tag file.
Replace <jsp:include page="part.jsp"/> with <tags:part/> in container.jsp.

container.jsp (before transformation)

<p> Container Page </p>

<jsp:include page="part.jsp"/>

container.jsp (after transformation)

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

<p> Container Page </p>

<tags:part/>

part.jsp (before transformation)

<%@ page language="java" %>

<p> Included Page </p>

<%= pageContext.getClass().getName() %>

part.tag (after transformation)

<%@ tag language="java" %>

<p> Invoked tag </p>

<%= jspContext.getClass().getName() %>

Example based on <jsp:include> and <%@include%>. The main page of this example (main.jsp) includes two page fragments (header.jspf and footer.jspf) using the <%@include%> directive. The main page also includes another page (content.jsp) using <jsp:include>. The main.jsp page provides three parameters (a, b, and c) to the included page with the help of the <jsp:param> standard action. The included content.jsp page outputs the values of the three parameters with ${param.a}, ${param.b}, and ${param.c}.

main.jsp

<%@ include file="header.jspf" %>

<jsp:include page="content.jsp">
    <jsp:param name="a" value="1"/>
    <jsp:param name="b" value="2"/>
    <jsp:param name="c" value="3"/>
</jsp:include>

<%@ include file="footer.jspf" %>

header.jspf

<p> Header </p>

footer.jspf

<p> Footer </p>

content.jsp

<p> Included Content </p>

<p> Parameters - ${param.a}, ${param.b}, ${param.c} </p>

Output

figure 1

Example based on tag files. The JSP page of this example (main.jsp) uses the <%@taglib%> directive to declare the library that contains a tag file (wrapper.tag), indicating its prefix (tags) and the directory where the tag file was created (/WEB-INF/tags). The main.jsp page invokes the wrapper.tag file using the <tags:wrapper> custom tag, which has three attributes (a, b, and c) and some body content (<p> Wrapped Content </p>). Note that you may not use any scripting elements between <tags:wrapper> and </tags:wrapper> in the JSP page. Scripting elements include JSP 1.x declarations (<%!...%>), JSP 1.x expressions (<%=...%>), and scriptlets (<%...%>), which are all supported by JSP 2.0. Note that tag files may use scripting elements like any regular JSP page. In fact, it would be a good idea to move your Java code from existing JSP pages into tag files if you don't like building JSP libraries based on Java tag handlers. This would make your Java scriptlets reusable and your Web pages more readable. The JSP container does the hard work for you, generating the tag handler classes automatically.

The wrapper.tag file is the replacement of the three included pages from the previous example (header.jspf, footer.jspf, and content.jsp). The tag file declares three attributes using the <%@attribute%> directive and outputs a header, a footer, and the values of the attributes (${a}, ${b}, and ${c}). The wrapper.tag file executes the body of <tags:wrapper> from main.jsp with the help of the new <jsp:doBody> standard action. The tag file decides if and when the body of <tags:wrapper> is executed. If wrapper.tag doesn't use <jsp:doBody>, the body of <tags:wrapper> is ignored. The tag file could also use <jsp:doBody> multiple times to execute the body more than once.

main.jsp

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

<tags:wrapper a="1" b="2" c="3">

    <p> Wrapped Content </p>

</tags:wrapper>

wrapper.tag

<%@ tag body-content="scriptless" %>
<%@ attribute name="a" required="true" %>
<%@ attribute name="b" required="true" %>
<%@ attribute name="c" required="true" %>

<p> Header </p>

<jsp:doBody/>

<p> Attributes - ${a}, ${b}, ${c} </p>

<p> Footer </p>

Output

figure 2

Tag files versus <jsp:include> and <%@include%>. Custom tags invoking tag files have a more compact syntax than the combination of <jsp:include> and <jsp:param>. Custom tags can also be nested and indented in a natural way, making the code easy to read. The only thing that might be perceived as a disadvantage is the restriction that doesn't allow you to use scripting elements in the body of a custom tag that invokes a tag file. This might actually be a plus, considering that scriptless Web pages can be maintained more easily. If you have to mix JSP and Java code, you can move the Java scriptlets into the tag file.

More tag file benefits are offered by the <%@tag%>, <%@attribute%>, and <%@variable%> directives:

  • Attribute declarations make the code of the tag file more readable because you can find out the names of the attributes by just looking at the tag file's header. In the case of the included pages, parameters are not declared at the beginning of the page. If you have to use a page developed by someone else and parameters aren't documented, you might have to analyze the source code of the entire page.
  • By default, all attributes are optional. If an attribute is required, you should specify this with <%@attribute name="..." required="true"%>. By default, a custom tag invoking a tag file may contain scriptless JSP code. If the tag file doesn't use <jsp:doBody>, you should specify that the tag file ignores the body content with <%@tag body-content="empty"%>. This declaration prevents the accidental use of JSP content between <prefix:tagFileName> and </prefix:tagFileName>. In this case, the tag file should be invoked with an empty tag like <prefix:tagFileName/>. By indicating the required attributes and whether the body content is used or not, you can prevent many frequent errors, reducing the time you spend debugging JSP pages. Support for such declarations is not provided for the included JSP pages.
  • You may also specify the type of an attribute using something like <%@attribute type="className"%>. The value of the attribute will be converted to the specified type automatically, and an error occurs if the conversion fails. In the case of an included page, all parameters are strings and you must code any necessary conversion.
  • The new <%@variable%> directive provides hints to the JSP container that automates tasks such as the export and synchronization of the JSP variables, which helps the communication between the invoked tag file and the invoking page. We'll see how this works later in this article. In the case of the included JSP pages, the interpage communication must be implemented manually, which usually requires more coding.
  • The <%@attribute%> and <%@variable%> directives enable features that are not available in included JSP pages, such as dynamic attributes collected in a java.util.Map, attributes that represent JSP fragments, and variables whose names are specified by attributes. The examples of the next section use these features.

Using the Advanced JSP 2.0 Features

The following examples show how to exploit the true power of the JSP tag files. You'll learn how to handle dynamic attributes, how to use variables as output parameters, how to use attributes to name variables, and how to pass JSP fragments to a tag file using attributes. Then, we'll use these advanced JSP features to build another set of tag files that query and update a database using the JSP expression language within SQL statements.

Handling dynamic attributes in tag files. This example contains a tag file (dynamicAttr.tag) that accepts dynamic attributes in addition to several other attributes (listTag, itemTag, and separator) declared with the <%@attribute> directive. A JSP page (dynamicAttr.jsp) invokes the tag file using the <demo:dynamicAttr> tag. Declared and dynamic attributes can be mixed because the order of the attributes doesn't matter.

By default, a tag file accepts only declared attributes. In order to enable the support for dynamic attributes, the tag file must use <%@tag dynamic-attributes="varName"%>, providing the name of a variable that will hold all dynamic attributes in a java.util.Map. In our example, this variable was named attrMap. The dynamicAttr.tag file iterates over the items of attrMap with the <c:forEach> action of JSTL, getting the name and value of each attribute with ${attr.key} and ${attr.value}.

If you look at the output, you'll observe that the six dynamic attributes (first, second, ... sixth) are not obtained in the order specified by the <demo:dynamicAttr> tag in dynamicAttr.jsp. This happens because the java.util.Map implementation controls the order of its items. Therefore, tag files must be prepared to handle their dynamic attributes in an arbitrary order.

The dynamicAttr.tag file uses the <jsp:element> standard action in order to generate HTML elements whose names are obtained at runtime. This new JSP 2.0 feature is not related to dynamic attributes, but we use it in this example to generate an HTML list (listTag is "ul" and itemTag is "li").

dynamicAttr.tag

<%@ tag dynamic-attributes="attrMap" %>
<%@ attribute name="listTag" required="true" %>
<%@ attribute name="itemTag" required="true" %>
<%@ attribute name="separator" required="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<p> Dynamic Attributes: </p>
<jsp:element name="${listTag}">
    <c:forEach var="attr" items="${attrMap}">
        <jsp:element name="${itemTag}">
            ${attr.key} ${separator} ${attr.value}
        </jsp:element>
    </c:forEach>
</jsp:element>

dynamicAttr.jsp

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

<demo:dynamicAttr separator="="
    first="1" second="2" third="3"
    listTag="ul" itemTag="li"
    fourth="4" fifth="5" sixth="6"/>

Output

figure 3

Exporting variables from a tag file to a JSP page. The <c:set> tag of JSTL allows you to create and modify variables in any of the four JSP scopes (page, request, session, and application). A JSP page shares the request, session, and application scopes with the tag files that are invoked from the JSP page. The page scope, which is the default scope of <c:set>, is not shared. Therefore, you may view the variables created in the request, session, and application scopes as "global variables," while those created in the page scope act as "local variables."

Because each tag file has its own page scope, the local variables of the invoking page are not accessible in the invoked tag file and vice versa. The JSP page may use tag attributes and global variables to pass parameters to the invoked tag. For each attribute, the JSP container creates a local variable in the page scope of the tag file, which allows you to get the attribute's value using ${attributeName} constructs. Therefore, tag attributes act as parameters passed by value, while global variables act as parameters passed by reference. If the tag file modifies the value of a global variable, the change affects the JSP page.

A tag file may "export" its local variables to the invoking page using the <%@variable%> directive. The name of a variable can be specified in the tag file using <%@variable name-given="..."%>, or the JSP page can provide the variable's name, as we'll see in the varAttr.jsp example. The type of the variable can be indicated with <%@variable variable-class="..."%>.

After declaring a variable in a tag file with <%@variable%>, you have to set its value with <c:set>. The JSP container creates another variable with the given name in the JSP page, and this second variable is initialized with the value of the corresponding variable from the tag file. Changing the value of the variable from the JSP page does not affect the corresponding variable from the tag file. The JSP container, however, updates the variable from the JSP page depending on the scope attribute from <%@variable%>, which can be AT_BEGIN, NESTED, or AT_END.

If scope is AT_END, the variable from the JSP page is initialized with the value of the tag file variable after the execution of the tag file. If scope is AT_BEGIN, the variable from the JSP page is updated before <jsp:doBody>, before each <jsp:invoke>, and at the end of the tag file's execution. If scope is NESTED, the JSP variable is updated only before <jsp:doBody> and before each <jsp:invoke> with the value of the tag file variable. After the execution of the tag file, in the NESTED case, the JSP variable is restored to the value it had before the invocation of the tag file.

For a better understanding of how variables work, let's use them in an example that is made of a tag file (varScope.tag), a JSP page (varScope.jsp), and several fragments (varScopeRow.tagf, varScopeHeader.jspf, varScopeFooter.jspf, and varScopeRow.jspf). The fragments are used only to create an HTML table. We have to use <%@include%> in this example to separate the logic from the presentation while studying the tag file variables. Using tag files for presentation is the best solution in most situations, but in this case the additional tag files would interfere with the variable "exporting" mechanisms that we want to study.

The tag file uses a "global" variable (GV), an undeclared "local" variable (LV), and three variables (BV, NV, and EV) declared with <%@variable%>. The varScope.tag file initializes these variables with "TAG A", then it uses <jsp:doBody>, and finally it changes the values of all variables to the "TAG B" string. The varScope.jsp page initializes its own set of variables with "JSP A" and then invokes the tag file with <demo:varScope>, changing the values of the variables to "JSP B" within the body of <demo:varScope>. The resulting output is included after the source code.

varScope.tag

<%@ variable name-given="BV" scope="AT_BEGIN" %>
<%@ variable name-given="NV" scope="NESTED" %>
<%@ variable name-given="EV" scope="AT_END" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="tagPhase" value="TAG: at begin"/>
<%@ include file="varScopeRow.tagf" %>

<c:set var="GV" value="TAG A" scope="request"/>
<c:set var="LV" value="TAG A"/>
<c:set var="BV" value="TAG A"/>
<c:set var="NV" value="TAG A"/>
<c:set var="EV" value="TAG A"/>

<c:set var="tagPhase" value="TAG: before doBody"/>
<%@ include file="varScopeRow.tagf" %>

<jsp:doBody/>

<c:set var="tagPhase" value="TAG: after doBody"/>
<%@ include file="varScopeRow.tagf" %>

<c:set var="GV" value="TAG B" scope="request"/>
<c:set var="LV" value="TAG B"/>
<c:set var="BV" value="TAG B"/>
<c:set var="NV" value="TAG B"/>
<c:set var="EV" value="TAG B"/>

<c:set var="tagPhase" value="TAG: at end"/>
<%@ include file="varScopeRow.tagf" %>

varScopeRow.tagf

<tr>
    <td width="25%">${tagPhase}</td>
    <c:set var="allEqual" value=
        "${LV == GV and LV == BV and LV == NV and LV == EV}"/>
    <c:if test="${allEqual}">
        <td align="center" width="75%" colspan="5">
            <i> ${LV != null ? LV : "null"} </i>
        </td>
    </c:if>
    <c:if test="${!allEqual}">
        <td align="center" width="15%">
            ${GV != null ? GV : "null"}
        </td>
        <td align="center" width="15%">
            ${LV != null ? LV : "null"}
        </td>
        <td align="center" width="15%">
            ${BV != null ? BV : "null"}
        </td>
        <td align="center" width="15%">
            ${NV != null ? NV : "null"}
        </td>
        <td align="center" width="15%">
            ${EV != null ? EV : "null"}
        </td>
    </c:if>
</tr>

varScope.jsp

<%@ taglib prefix="demo" tagdir="/technology/WEB-INF/tags/demo" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ include file="varScopeHeader.jspf" %>

<c:set var="GV" value="JSP A" scope="request"/>
<c:set var="LV" value="JSP A"/>
<c:set var="BV" value="JSP A"/>
<c:set var="NV" value="JSP A"/>
<c:set var="EV" value="JSP A"/>

<c:set var="jspPhase" value="JSP: before start tag"/>
<%@ include file="varScopeRow.jspf" %>

<demo:varScope>

    <c:set var="jspPhase" value="JSP: after start tag"/>
    <%@ include file="varScopeRow.jspf" %>

    <c:set var="GV" value="JSP B" scope="request"/>
    <c:set var="LV" value="JSP B"/>
    <c:set var="BV" value="JSP B"/>
    <c:set var="NV" value="JSP B"/>
    <c:set var="EV" value="JSP B"/>

    <c:set var="jspPhase" value="JSP: before end tag"/>
    <%@ include file="varScopeRow.jspf" %>

</demo:varScope>

<c:set var="jspPhase" value="JSP: after end tag"/>
<%@ include file="varScopeRow.jspf" %>

<%@ include file="varScopeFooter.jspf" %>

varScopeHeader.jspf

<table border="1">

<tr>
    <th width="25%">
         
    </th>
    <th align="center" width="15%">
        GV<br>Global Var<br>in Request<br>Scope
    </th>
    <th align="center" width="15%">
        LV<br>Local Var<br>Not<br>Declared
    </th>
    <th align="center" width="15%">
        BV<br>Tag Var<br>Scope:<br>AT_BEGIN
    </th>
    <th align="center" width="15%">
        NV<br>Tag Var<br>Scope:<br>NESTED
    </th>
    <th align="center" width="15%">
        EV<br>Tag Var<br>Scope:<br>AT_END
    </th>
</tr>

varScopeFooter.jspf

</table>

varScopeRow.jspf

<tr>
    <td width="25%">${jspPhase}</td>
    <c:set var="allEqual" value=
        "${LV == GV and LV == BV and LV == NV and LV == EV}"/>
    <c:if test="${allEqual}">
        <td align="center" width="75%" colspan="5">
            <i> ${LV != null ? LV : "null"} </i>
        </td>
    </c:if>
    <c:if test="${!allEqual}">
        <td align="center" width="15%">
            ${GV != null ? GV : "null"}
        </td>
        <td align="center" width="15%">
            ${LV != null ? LV : "null"}
        </td>
        <td align="center" width="15%">
            ${BV != null ? BV : "null"}
        </td>
        <td align="center" width="15%">
            ${NV != null ? NV : "null"}
        </td>
        <td align="center" width="15%">
            ${EV != null ? EV : "null"}
        </td>
    </c:if>
</tr>

Output

figure 4

Using attributes to provide names for variables. The previous example showed how to use variables whose names are given by the tag file. The following example has a tag file (varAttr.tag) that creates a variable whose name is provided by a JSP page (varAttr.jsp) as the value of a tag attribute (v). In order to be able to operate with the variable, the tag file defines an alias (a) using <%@variable name-from-attribute="v" ... alias="a" ...%>.

The varAttr.tag file sets the value of the variable with <c:set var="a" value="..."/>, outputs the variable's name with ${v}, and outputs the variable's value with ${a}. The varAttr.jsp page invokes the tag file with <demo:varAttr v="x"/> and works with the variable named x. Note that the JSP container creates the x variable automatically, setting it to the value of a, which is 123.

varAttr.tag

<%@ attribute name="v" required="true" %>
<%@ variable name-from-attribute="v"
    variable-class="java.lang.Long"
    alias="a" scope="AT_END" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="a" value="${123}"/>

<p> TAG: ${v} = ${a} </p>

varAttr.jsp

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

<demo:varAttr v="x"/>

<p> JSP: x = ${x} </p>

Output

figure 5

Invoking JSP fragments from tag files. You've learned so far that tag files support two attribute types: simple declared attributes and dynamic attributes. There is a third type called fragment attributes. The JSP page of this example (fragmentAttr.jsp) invokes a tag file (fragmentAttr.tag) with <demo:fragmentAttr>, providing two simple attributes (attr1 and attr2) and a fragment attribute (template). All three attributes are declared in the tag file with <%@attribute%>, but only template is declared as a fragment attribute with fragment="true".

The fragmentAttr.jsp page uses the <jsp:attribute> and <jsp:body> standard actions to define the two JSP fragments that are invoked from the tag file with <jsp:invoke> and <jsp:doBody>, respectively. In addition, the tag file declares a nested variable (data) with <%@variable%>. The value of the variable is set by the tag file with <c:set>, and the two JSP fragments output the variable's value with ${data}.

fragmentAttr.tag

<%@ attribute name="template" fragment="true" %>
<%@ attribute name="attr1" %>
<%@ attribute name="attr2" %>
<%@ variable name-given="data" scope="NESTED" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="data" value="${attr1}"/>
<jsp:invoke fragment="template"/>

<c:set var="data" value="${attr1} - ${attr2}"/>
<jsp:doBody/>

<c:set var="data" value="${attr2}"/>
<jsp:invoke fragment="template"/>

<c:set var="data" value="${attr2} - ${attr1}"/>
<jsp:doBody/>

fragmentAttr.jsp

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

<demo:fragmentAttr attr1="value1" attr2="value2">
    <jsp:attribute name="template">
        <p> Template: ${data} </p>
    </jsp:attribute>
    <jsp:body>
        <p> Body Content: ${data} </p>
    </jsp:body>
</demo:fragmentAttr>

Output

figure 6

Using Tag Files to Update and Query Databases

When developing complex enterprise applications, many prefer to use the Enterprise JavaBeans (EJB) technology, letting the application server manage the object persistence. Others might prefer to use the Java Database Connectivity (JDBC) standard API in order to optimize the database access manually. These solutions might not be suitable in the case of a simple database-backed Web site that just needs to be prototyped as fast as possible using JSPs. A tag library providing database access features can be the best solution if you just need to query and update a simple database.

JSTL has a set of simple SQL tags that we'll use in the following examples. Instead of invoking the JSTL SQL tags directly from your pages, it's a good idea to separate the presentation from the database access scripts using tag files. This separation allows you to switch easily to another database connectivity solution, at a later time, in order to increase the performance and scalability of your application.

JSTL is not the only tag library providing database access features. Oracle Application Development Framework (ADF) offers a JSP library called Business Components Data Tags that contains a more advanced set of database-related JSP tags. Note that the set of ADF features is much broader, making Oracle ADF suitable for simple and complex applications. Oracle ADF is bundled together with JDeveloper 10g, which has visual design tools for ADF.

The following six examples create a table, insert three rows, update a row, delete another row, perform a couple of queries, and drop the table. Everything is done with generic tag files that don't depend on the table's structure.

Step 1: Create table. The JSP page of this example (create.jsp) invokes a tag file (create.tag) to create a table whose structure is provided as the body of the <db:create> custom tag. The table attribute specifies the name of the table (People). After creating the table, the JSP page outputs a message notifying the user that the operation was completed:

<db:create table="People">
    userID INTEGER,
    name VARCHAR2(60),
    email VARCHAR2(60)
</db:create>

<p> Table created. </p>

The create.tag file includes a tag fragment (init.tagf) that sets a javax.sql.DataSource variable named tags_db_dataSource within the application scope with the help of the <sql:setDataSource> tag of JSTL. The variable is created only if it doesn't already exist and is used in the tag file with the <sql:update> tag of JSTL, which executes the SQL statement from its body. The create.tag file builds the CREATE TABLE statement dynamically, getting the table's name with ${table} and inserting the table's structure with <jsp:doBody>:

<sql:update dataSource="${tags_db_dataSource}">
    CREATE TABLE ${table} ( <jsp:doBody/> )
</sql:update>

In order to run the examples, you'll have to configure a datasource named dbtags. A reference to this resource is declared in the Web application's descriptor (web.xml):

<resource-ref>
    <res-ref-name>jdbc/dbtags</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

The init.tagf fragment gets the path to the resource from an initialization parameter that is defined in web.xml too:

<context-param>
    <param-name>tags_db_dataSource</param-name>
    <param-value>jdbc/dbtags</param-value>
</context-param>

If you want to use another datasource, you just have to replace dbtags with the name of your datasource in the web.xml file.

init.tagf

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

<c:if test="${applicationScope.tags_db_dataSource == null}">
    <sql:setDataSource
        dataSource="${initParam.tags_db_dataSource}"
        var="tags_db_dataSource" scope="application"/>
</c:if>

create.tag

<%@ tag body-content="scriptless" %>
<%@ attribute name="table" required="true" %>
<%@ include file="init.tagf" %>

<sql:update dataSource="${tags_db_dataSource}">
    CREATE TABLE ${table} ( <jsp:doBody/> )
</sql:update>

create.jsp

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

<db:create table="People">
    userID INTEGER,
    name VARCHAR2(60),
    email VARCHAR2(60)
</db:create>

<p> Table created. </p>

Step 2: Insert. Once you have a table, you can insert one row at a time using the <db:insert> tag, which invokes a tag file (insert.tag) with the following syntax:

<db:insert table="..." column1="value1" column2="value2" .../>

An example JSP page (insert.jsp) uses <db:insert> to insert three rows into the People table. At each invocation, the tag file builds and executes an SQL statement using the following syntax:

INSERT INTO table (column1, column2, ...) VALUES (value1, value2, ...) 

The column names and values are specified as dynamic attributes so that <db:insert> can be used with any table, no matter how its columns are named. The tag file uses <c:forEach> to iterate over the entries of a java.util.Map instance (columnAttr) holding the dynamic attributes of <db:insert>. The name of each dynamic attribute represents a column name and is obtained with ${v_entry.key}. The tag file gets the values that must be inserted into the table with ${v_entry.value}.

Within the body of <sql:update>, the tag file builds a list of column names (v_columnNames) and another list containing parameter markers (v_paramMarkers). The <sql:param> tag of JSTL cooperates with <sql:update> transmitting the values of the SQL parameters. After the <c:forEach> loop, which produces no output, the tag file generates the SQL statement with

INSERT INTO ${table} (${v_columnNames}) VALUES (${v_paramMarkers})

When the tag file is invoked by insert.jsp, the <sql:update> tag evaluates its body obtaining

INSERT INTO People (userID, name, email) VALUES (?, ?, ?)

Then, <sql:update> executes the above SQL statement using the JDBC API and passing the parameter values that were transmitted by <sql:param>. Note that the columns and their corresponding values may appear in a different order because they are retrieved from a java.util.Map instance. Therefore, we have to make sure that the SQL statement does not rely on the order of the columns.

insert.tag

<%@ tag body-content="empty" dynamic-attributes="columnAttr" %>
<%@ attribute name="table" required="true" %>
<%@ include file="init.tagf" %>

<c:set var="v_columnNames" value=""/>
<c:set var="v_paramMarkers" value=""/>
<c:set var="v_separator" value=""/>
<sql:update dataSource="${tags_db_dataSource}">
    <c:forEach var="v_entry" items="${columnAttr}">
        <sql:param value="${v_entry.value}"/>
        <c:set var="v_columnNames"
            value="${v_columnNames}${v_separator}${v_entry.key}"/>
        <c:set var="v_paramMarkers"
            value="${v_paramMarkers}${v_separator}${'?'}"/>
        <c:set var="v_separator" value=", "/>
    </c:forEach>
    INSERT INTO ${table} (${v_columnNames})
        VALUES (${v_paramMarkers})
</sql:update>

insert.jsp

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

<db:insert table="People" userID="1" name="John Smith"
    email="JohnSmith@company.com" />

<db:insert table="People" userID="2" name="Mark Johnson"
    email="MarkJohnson@company.com" />

<db:insert table="People" userID="3" name="Bill Davis"
    email="BillDavis@company.com" />

<p> Rows inserted. </p>

Step 3: Update. The update.jsp page uses <db:update> to change the email value of one of the rows inserted with insert.jsp. The update.tag file is similar to insert.tag, but this time the <sql:update> tag of JSTL is used to execute an UPDATE statement:

UPDATE table SET column1=value1, column2=value2, ... WHERE ...

The columns and their values are specified as dynamic attributes using the following syntax:

<db:update table="..." column1="value1" column2="value2" ... 
    where="..."/>

The tag file uses the dynamic attributes to build a list (v_setList) containing column=? constructs. The column values are transmitted to <sql:update> with the help of <sql:param> as in the previous example. Then, update.tag generates the SQL statements with

 
UPDATE ${table} SET ${v_setList} WHERE ${where}

When the tag file is invoked by update.jsp, the <sql:update> tag evaluates its body obtaining

UPDATE People SET email=? WHERE userID=2

Unlike the <db:insert> tag that may be used to insert only one row at a time, the <db:update> tag can be used to update multiple rows with a single invocation.

update.tag
Resources

Use the following resources to test the examples and learn more about the JSP 2.0 tag files and about the JSTL Core and SQL tags.

Download the Source Code
The tagfiles_src.zip file contains the examples of this article. In order to run them, you need J2SE, a J2EE 1.4 application server, JSTL 1.1, and a database server.

Download OC4J 10g
Oracle Application Server Containers for J2EE 10g fully implement the J2EE 1.4 specs, which include JSP 2.0. You may use OC4J 10g (10.0.3) to test the examples. It works with all major database servers, including—of course—Oracle Database.

Download JSTL 1.1
Before deploying the examples, download JSTL and copy jstl.jar and standard.jar into the WEB-INF/lib directory of the Web application. Also, don't forget to configure the dbtags datasource and make sure that the proper database driver is available.

Read the JSP 2.0 Specification
The JSP 2.0 specification has an entire chapter dedicated to tag files ("Part I: Chapter JSP.8 Tag Files"). The new standard actions introduced by JSP 2.0 are described in another chapter ("Part I: Chapter JSP.5 Standard Actions").

Read the JSTL 1.1 Specification
The JSTL specification describes the <c:set>, <c:if>, <c:forEach>, <sql:setDataSource>, <sql:query>, <sql:update>, and <sql:param> tags used in the examples of this article.

Related Articles and Downloads

How-To: Add a Custom JSP Tag Library to JDeveloper 10g

Oracle JDeveloper 10g

JSP Sample Code

Tutorial: Understanding the New Features of JSP 2.0, "Tag Files" module

<%@ tag body-content="empty" dynamic-attributes="columnAttr" %>
<%@ attribute name="table" required="true" %>
<%@ attribute name="where" required="false" %>
<%@ include file="init.tagf" %>

<c:set var="v_setList" value=""/>
<c:set var="v_separator" value=""/>
<sql:update dataSource="${tags_db_dataSource}">
    <c:forEach var="v_entry" items="${columnAttr}">
        <sql:param value="${v_entry.value}"/>
        <c:set var="v_setList"
            value="${v_setList}${v_separator}${v_entry.key}=?"/>
        <c:set var="v_separator" value=", "/>
    </c:forEach>
    UPDATE ${table} SET ${v_setList}
    <c:if test="${!empty where}"> WHERE ${where} </c:if>
</sql:update>

update.jsp

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

<db:update table="People"
    email="markj@company.com" where="userID=2"/>

<p> Row updated. </p>

Step 4: Delete. The following tag file (delete.tag) can be used to delete one or more rows, as in the delete.jsp page.

delete.tag

<%@ tag body-content="empty" %>
<%@ attribute name="table" required="true" %>
<%@ attribute name="where" required="false" %>
<%@ include file="init.tagf" %>

<sql:update dataSource="${tags_db_dataSource}">
    DELETE FROM ${table}
    <c:if test="${!empty where}"> WHERE ${where} </c:if>
</sql:update>

delete.jsp

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

<db:delete table="People" where="userID=1"/>

<p> Row deleted. </p>

Step 5: Select. The next tag file (select.tag) uses the <sql:query> tag of JSTL to execute SELECT statements. The syntax of <db:select> is included below:

<db:select var="row" table="..." columns="..." 
    where="..." groupBy="..." having="..." orderBy="...">

    process the result of the query, one row at a time

</db:select>

The <db:select> tag builds and executes the following SQL statement, whose clauses are specified as the attributes of the tag.

SELECT columns FROM table WHERE ... 
    GROUP BY ... HAVING ... ORDER BY ...

When <db:select> appears in a JSP page, its body is invoked for each row with the <jsp:doBody> action form select.tag. The JSP page gets the row data from a java.util.Map created by the tag file with JSTL. For example, the values of the current row can be obtained with JSP constructs like ${row.userID}, ${row.name}, and ${row.email}, assuming that the JSP page invokes the tag file specifying that the variable holding the data should be named row:

<db:select var="row" table="People" columns="*">
    ${row.userID} ... ${row.name} ... ${row.email} 
</db:select>

The select.jsp page invokes the tag file twice. First it uses <db:select> to iterate over the relational table's rows generating an HTML table, and then the JSP page uses <db:select> to count the rows of the People table.

select.tag

<%@ tag body-content="scriptless" %>
<%@ attribute name="var" required="true" %>
<%@ variable name-from-attribute="var"
    alias="v_row" scope="NESTED" %>
<%@ attribute name="table"   required="true"  %>
<%@ attribute name="columns" required="false" %>
<%@ attribute name="where"   required="false" %>
<%@ attribute name="groupBy" required="false" %>
<%@ attribute name="having"  required="false" %>
<%@ attribute name="orderBy" required="false" %>
<%@ include file="init.tagf" %>

<sql:query dataSource="${tags_db_dataSource}" var="v_result">
    SELECT ${empty columns ? "*" : columns} FROM ${table}
    <c:if test="${!empty where}">   WHERE    ${where}   </c:if>
    <c:if test="${!empty groupBy}"> GROUP BY ${groupBy} </c:if>
    <c:if test="${!empty having}">  HAVING   ${having}  </c:if>
    <c:if test="${!empty orderBy}"> ORDER BY ${orderBy} </c:if>
</sql:query>

<c:forEach var="v_row" items="${v_result.rows}">
    <jsp:doBody/>
</c:forEach>

select.jsp

<%@ taglib prefix="db" tagdir="/technology/WEB-INF/tags/db" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<table border="2" cellspacing="1" cellpadding="3">
    <tr>
        <th> User ID </th>
        <th> Name </th>
        <th> Email </th>
    </tr>
    <db:select var="row" table="People" orderBy="name">
        <tr>
            <td> ${row.userID} </td>
            <td> ${row.name} </td>
            <td> ${row.email} </td>
        </tr>
    </db:select>
</table>

<db:select var="c" table="People" columns="count(*) as n">
    <p> ${c.n} people. </p>
</db:select>

Output

figure 7

Step 6: Drop table. If you want to execute again the JSP examples, you have to drop the table with the drop.jsp page that uses the drop.tag file.

drop.tag

<%@ tag body-content="scriptless" %>
<%@ attribute name="table" required="true" %>
<%@ include file="init.tagf" %>

<sql:update dataSource="${tags_db_dataSource}">
    DROP TABLE ${table}
</sql:update>

drop.jsp

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

<db:drop table="People"/>

<p> Table dropped. </p>

Conclusion

Tag files are the easy-to-use solution that Web developers need in order to build custom tag libraries. The transition from Java tag handlers to JSP tag files is similar to the transition from Servlets to JSP that happened several years ago. When developing Java Web applications, most of the dynamic content is now generated with JSP, but Servlets are still used as an underlying technology for JSPs and there are cases when Servlets are preferred instead of JSP. Similarly, Java tag handlers are still needed as an underlying technology for tag files and for building sophisticated tag libraries such as JSTL. Most tag libraries, however, will be built using tag files because they simplify the development process.


Andrei Cioroianu (
devtools@devsphere.com) is the founder of Devsphere (www.devsphere.com), a provider of Java frameworks, XML consulting, and Web development services. Andrei Cioroianu has written many Java articles published by ONJava, JavaWorld, and Java Developer's Journal. He also coauthored the books Java XML Programmer's Reference and Professional Java XML (both from Wrox Press).


Please rate this document:

Excellent Good Average Below Average Poor


Send us your comments

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy