With the introduction of JSP 2.0, knowledge of Java is no
longer a prerequisite to create a custom tag action. JSP 2.0's Simple tag extension
allows page authors to write tag extensions using only JSP syntax. Tag files
bring the power of reuse to the basic page author, who is presentation-focused
and is not required to know Java. Tag files are essentially source files that
facilitate the abstraction of a fragment of JSP code, making it reusable via
a custom action. Even for page authors or tag library developers who know Java,
writing tag files is more convenient when developing tags that primarily output
template text. When used together with JSP Fragments and Simple Tag Handlers,
these concepts have the ability to simplify JSP development substantially, even
for developers.
The required file extension for a tag file is .tag or .tagx. As is the case with JSP files, the actual
tag may be composed of a top file that includes other files that contain either a complete tag or a
segment of a tag file. Similar to the recommended extension, .jspf, for a segment of a JSP file ,
the recommended extension for a segment of a tag file is .tagf. Tag files can contain nothing more
than template text, or they can contain JSP and markup code.
A tag file can forward to a page via the
standard action, with the forward being handled through the request dispatcher
(similar to JSP). When RequestDispatcher.forward() returns, the decision
to stop processing the tag file and throw a javax.servlet.jsp.SkipPage-Exception
is left to the discretion of the container.
Tag File Syntax
The syntax for tag files is similar to that of the JSP Syntax,
save for the following:
While some tag file specific directives are available,
some directives are unavailable or have limited availability
The <jsp:doBody> and <jsp:invoke> standard
actions can only be used in tag files. We will look at these separately in
the section titled Standard action in Tag files
Following is a tabular listing of directives available to
tag files:
Directive
Description/Limitations
Page
This directive is not available to tag files. Since a tag file is not
considered a page, a tag file needs to be used instead. Note that if this
directive is used in a tag file, it'll result in a translation error.
Taglib
Available to tag files, this directive is similar to JSP pages
include
Available to tag files, this directive is similar to JSP pages. The included
file must comply with syntax valid for a tag file. Otherwise, a translation
error will occur.
Tag
Available and applicable only to tag files.
Using this directive in a JSP will cause a translation error.
attribute
Available and applicable only to tag files.
Using this directive in a JSP will cause a translation error.
variable
Available and applicable only to tag files.
Using this directive in a JSP will cause a translation error.
Using classic tag handlers indirectly from a tag file binds
the use of the tag file to Servlet environments, because the tag interface
relies on PageContext (which is Servlet centric). So it pays to wary when a
classic tag handler that implements the tag interface is invoked from
a tag file. Note that SimpleTag extensions can be used in environments other
than Servlet.
Standard action in Tag files
There are two standard actions that can only be used in tag
files - they are <jsp:invoke> and <jsp:doBody>. In this section,
we will look into these two standard actions in some detail:
<jsp:doBody>
As mentioned earlier, the <jsp:doBody> standard action
can only be used in tag files, and must result in a translation error if used
in a JSP. The standard action of <jsp:doBody> is very similar to <jsp:invoke>,
with the only difference being that <jsp:doBody> operates on the body
of the tag instead of a specific fragment passed as an attribute. It invokes
the body of the tag, sending the output of the result to the JspWriter, or to
a scoped attribute that can be examined and manipulated. The body of a tag is
passed to the simple tag handler as a JspFragment object. If the <jsp:doBody>
contains a non-empty body, a translation error will occur.
Following is a tabular listing of the directives available
to the <jsp:doBody> standard action:
Attribute
Description
var
This attribute is the name used to identify
a fragment during the tag invocation. This directive is mandatory
varReader
This is the name of a scoped
attribute to store the result of the fragment invocation in, as a java.lang.String
object. A translation error must occur if both var and varReader are specified.
If neither var nor varReader are specified, the result of the fragment goes
directly to the JspWriter, as described earlier in this section. This directive
is optional.
scope
This is the name of a scoped attribute to
store the result of the fragment invocation in, as a java.io.Reader object.
A translation error must occur if both var and varReader are specified.
If neither var nor varReader is specified, the result
of the fragment invocation goes directly to the JspWriter, as described
earlier in this section.
Since <jsp:doBody> only operates on the body of the
tag, there is no name attribute for this standard action. The var, varReader,
and scope attributes are all supported with the same semantics as for <jsp:invoke>.Fragments
are provided access to variables the same way for <jsp:doBody> as they
are for <jsp:invoke>. If no body was passed to the tag, <jsp:doBody>
will behave as though a body was passed in that produces no output.
<jsp:invoke>
To reiterate, the <jsp:invoke> standard action can only
be used in tag files and will result in a translation error if used in a JSP.
Following is a tabular listing of the directives available
to the <jsp:invoke> standard action:
Attribute
Description
fragment
This attribute is the name used to identify
a fragment during the tag invocation. This directive is mandatory.
var
This is the name of a scoped
attribute to store the result of the fragment invocation in, as a java.lang.String
object. A translation error must occur if both var and varReader are specified.
If neither var nor varReader are specified, the
result of the fragment goes directly to the JspWriter, as described in the
beginning of this section. This directive is optional.
varReader
This is the name of a scoped attribute to
store the result of the fragment invocation in, as a java.io.Reader object.
A translation error must occur if both var and varReader are specified.
If neither var nor varReader is specified, the result of the fragment invocation
goes directly to the JspWriter, as described in the beginning of this section.
This directive is optional.
scope
This is the scope in which to store the resulting
variable. A translation error must result if the value is not
one of page, request, session, or application. A translation error will
result if this attribute appears without specifying
either the var or varReader attribute as well. Note that a value of session
should be used with caution since not all
calling pages may be participating in a session. A container must throw
an IllegalStateException at runtime if scope is
session and the calling page does not participate in a session. Defaults
to page. This directive is optional.
The <jsp:invoke> standard action is used to invoke a
fragment (which is determined by the name attribute) and send the output of
the result to the JspWriter. The fragment is invoked using the JspFragment.invoke()
method. Null is passed as the Writer parameter to force the write (for the results
to be sent to, that is) to the JspWriter of the JspContent associated with the
JspFragment object. An example of using invoke is shown below:
<jsp:invoke fragment="Frag1" />
It's also possible to send the output to a page-scoped variable that can be
used later on the page for other manipulations, by using the var or varReader
attribute. When using either of these attributes a custom java.io.Writer is
passed instead of null. You can further tweak the Writer to specify whether
you want a String or a Reader.
The var attribute is a java.lang.String while the varReader attribute is a java.io.Reader
object. While String objects contain the content sent by the fragment to the
Writer, the Reader object can produce the content sent by the fragment to the
Writer. The Reader is resettable - if the reset() method is called, the result
of the invoked fragment can be re-read without re-executing the fragment.
The optional scope attribute can be used to set the resulting scoped variable.
Scope can be set to the standard JSP scopes including: page, request, session
or application. Here are two examples of how to use the var, varReader and the
scope attribute:
Some of the advantages of using tag files in JSP development:
Reuse mechanism for page authors - Tag files constitute
a flexible and efficient reuse mechanism for page authors by allowing better
customization of the included content as well as the nesting of tags.
Compatibility with tag handlers - Tag files are
very well suited for tag handlers that primarily output HTML content, similar
to how JSPs are well suited for replacing Servlets that primarily output HTML
content
Scriptlet free JSP pages - Tag files make for a
great way to hide ugly scriptlets, by helping to change the script-based JSP
code in a web application to much cleaner JSTL-style code with no scriptlets,
and with EL expressions instead of scripting expressions. Page authors can
easily abstract scriptlets into tag files and then invoke the tags. Later,
the scriptlet-based tag files can be converted to JSTL-style code or encapsulated
into Java tag handlers.
Rapid development - Dynamic recompilation of tag
files are supported in some containers like Tomcat 5.0 (worth noting that
the 5.x releases implement the Servlet 2.4 and JSP 2.0 specifications) With
this feature, you simply need to deploy your tag file in /WEB-INF/tags/ or
a subdirectory, and tweak it until it works, without having to recompile and
redeploy for each and every change during development.
Flexible Packaging - Tag files provide the advantage
of flexible packaging. The directory /WEB-INF/tags/ is now a standard directory
that is recognized by compliant containers. The JSP container will process
any file with the .tag extension that is present in this directory, or a subdirectory
of tags. The container will create an implicit TLD file as well as a simple
tag handler.
Options for packaging - Tag files can be packaged
in one of three ways:
In /WEB-INF/tags/ with no TLD. The custom actions are then imported into the
JSP using <%@ taglib prefix="..." tagdir="/WEB-INF/tags"
%>
In /WEB-INF/tags/ with a supplementary TLD. This allows for greater customization
of the tag file, and makes it transparent to the caller that the tag was implemented
as a tag file. The TLD would be imported using <%@ taglib prefix="..."
uri="..." %>
In /META-INF/tags/ in a JAR file with a TLD. This is ideal for tag files that
are part of a tag library in a JAR file that can simply be "dropped in"
to your web application. Note that Tag files that are bundled in a JAR require
a TLD, and the one's that are not defined in a TLD but appear in a JAR are
ignored by the web container.