Writing JSP Extensions
- I. Introduction
- Overview of Custom Tag Functionality
- Using custom tags in a JavaServer page
- II. Quick Start Guide
- III. Creating a Tag Library Descriptor
- Identifying the Tag Library
- Defining a Tag
- IV. Implementing a Tag Handler
- Tag handler API
- Tag handler Lifecycle
- Using Tag Attributes
- Defining New Scripting Variables
- Writing Cooperative Nested Tags
- Other Related Documents
- JavaSoft's JSP Overview Page
- Using WebLogic HTTP servlets
- JSP examples
- JSP tag extension examples
- Writing a Web Application
I. Introduction
JSP 1.1 introduced the ability to create and use custom tags in
JSP pages. Custom tags are an excellent way to abstract the
complexity of business logic from the presentation of Web pages in a
way that is easy for the Web author to use and control. You can use
custom JSP tag extensions in JSP pages to generate dynamic content,
and you can use a variety of Web development tools to create the
presentation.
The WebLogic Server fully supports the tag extension mechanism
described in the JSP 1.1
specification.
Overview of Custom Tag Functionality
You write a custom JSP tag by writing a Java class called the tag
handler. You write the tag handler class by implementing one of two
interfaces, Tag or BodyTag that define methods that are
invoked during the tag's lifecycle or by extending an abstract base class that
implements one of these interfaces. Extending an abstract base class relieves
the tag handler class from having to implement all methods in the
interfaces and also provides other convenient functionality. The
TagSupport and BodyTagSupport classes
implement these interfaces and are included in the API.
One or more custom JSP tags are described by a Tag Library,
defined by a Tag Library Descriptor (.tld) file. This file describes the syntax for each tag
and ties it to the Java classes that execute its functionality.
Using Custom Tags in a JavaServer Page
Referencing a Tag Library
To use a custom tag library from a JSP page, reference it with a
<%@ taglib %>
directive. For example:
<%@ taglib uri="/myco/taglib" prefix="mytaglib" %>
The JSP engine attempts to find the Tag Library
Descriptor file from the uri attribute (see the above example). This value of
this attribute can be either a simple string that is matched to a file
path defined by a <taglib-uri> element in your Web Application
deployment descriptor (the web.xml file), or simply a file path relative to the
document root.
The JSP engine first attempts to match the uri with a <taglib-uri> element. If this fails, it then
searches for the file given by the uri from the document root, or the root directory of
the Web Application. For more details on the <taglib-uri> elements, see the Developers Guide
Writing a Web Application.
If you are not using tag extensions within a Web Application, specify
the filename of the Tag Library Descriptor in the uri.
The prefix attribute
assigns a label to the tag library. You use this label to reference
its associated tag library when writing your pages using custom JSP
tags. For example, if the library (called mytaglib) from the example above defines a new tag
called newtag, you would use
the tag in your JSP page like this:
<mytaglib:newtag>
Using Custom Tags
A custom tag's format can be empty, called an empty tag, or
it can contain a body, called a body tag. Both types of tags
can accept a number of attributes that are passed to the Java class
that implements the tag. For more details, see Using tag attributes.
An empty tag takes the following form:
<mytaglib:newtag attr1="aaa" attr2="bbb" ... />
A body tag takes the following form:
<mytaglib:newtag attr1="aaa" attr2="bbb" ... >
body
</mytaglib:newtag>
A tag body can include more JSP syntax, and even other custom
JSP tags that also have nested bodies. Tags can be nested within each
other to any level. For example:
<mytaglib:tagA>
<h2>This is the body of tagA</h2>
You have seen this text <mytaglib:counter /> times!
<p>
<mytaglib:repeater repeat=4>
<p>Hello World!
</mytaglib:repeater>
</mytaglib:tagA>
- The preceding example uses three custom tags to illustrate the
ability to nest tags within a body tag. The tags function like this:
The body tag <mytaglib:tagA>
only sees the HTML output from its evaluated body. That is, the nested JSP
tags <mytaglib:counter> and
<mytaglib:repeater> are first
evaluated and their output becomes part of the evaluated body of the
<mytaglib:tagA> tag.
The body of a body tag is first evaluated as JSP and all tags that it
contains are translated, including nested body tags, whose bodies are
recursively evaluated. The result of an evaluated body can then be used
directly as the output of a body tag, or the body tag can determine its output
based on the content of the evaluated body.
The output generated from the JSP of a body tag is treated as plain
HTML. That is, the output is not further interpreted as JSP.
What Can Custom Tags Do?
Custom tags can perform the following tasks:
- Produce output. A tag's output is sent to the surrounding
scope. The scope can be one of the following:
- If the tag is included directly in the JSP page, then the
surrounding scope is the JSP page output.
- If the tag is nested within another parent tag, then the output
becomes part of the evaluated body of its parent tag.
- Define new objects that can be referenced and used as scripting
variables in the JSP page. A tag can introduce fixed-named scripting
variables, or can define a dynamically-named scripting variable with
the id attribute.
- Iterate over their body content until a certain condition is met.
Use iteration to create repetitive output, or to repeatedly invoke a
server side action.
- Determine whether the rest of the JSP page should be processed as
part of the request, or skipped.
Some Example Scenarios
The following scenarios demonstrate what you can do with custom
tags:
- An empty tag can perform server-side work based on its
attributes. The action that the tag performs can determine whether
the rest of the page is interpreted or some other action is taken,
such as a redirect. This function is useful for checking that users
are logged in before accessing a page, and redirecting them to a login
page if necessary.
- An empty tag can insert content into a page based on its
attributes. You can use such a tag to implement a simple page-hits
counter or another template-based insertion.
- An empty tag can define a server-side object that is available in
the rest of the page, based on its attributes. You can use this tag to
create a reference to an EJB, which is queried for data elsewhere in
the JSP page.
- A body tag has the option to process its output before the output
becomes part of the HTML page sent to the browser, evaluate that
output, and then determine the resulting HTML that is sent to the
browser. This functionality could be used to produce 'quoted HTML',
reformatted content, or used as a parameter that you pass to another
function, such as an SQL query, where the output of the tag is a
formatted result set.
- A body tag can repeatedly process its body until a particular
condition is met.
II. Quick Start Guide
The following is an overview of the steps required to create and use
custom JSP tags. These topics are covered in detail in this document:
- Write a tag handler class. When you use
a custom tag in your JSP, this class is invoked to do the work. A Tag
handler class implements one of two interfaces: javax.servlet.jsp.tagtext.BodyTag or javax.servlet.jsp.tagtext.Tag. Your
tag handler class is implemented as part of a tag library.
- Reference the tag library in your JSP source using the JSP <taglib> directive. A tag
library is a collection of JSP tags. Include this directive at the top
of your JSP source. The <taglib> directive defines a URI where a file
called the Tab Library Descriptor is located. (You can also reference
this file from a Web Application's deployment descriptor.)
- Write the Tag Library Descriptor (TLD). The TLD defines the tag
library and provides additional information about each tag, such as
the name of the tag handler class, attributes, and other information
about the tags
- Use your custom tag in your JSP.
III. Creating a Tag Library Descriptor
It makes sense for tags with related functionality to be grouped
together in a tag library. A tag library requires a tag library
Descriptor (TLD) file that describes the new tag extensions and
relates them to the Java classes defining them. The JSP engine and
authoring tools use the TLD to get information about the extensions.
TLD files are written in XML notation.
The syntax for a tag library Descriptor is specified in the
document type descriptor (DTD) available at:
http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd
Order the elements in the Tag Library Descriptor file as they are
defined in the DTD. This ordering is used in elements described in the
steps below. The XML parser will throw an exception if your TLD
elements are placed out-of-order.
The body of the TLD contains additional nested elements inside of
the
<taglib> ... </taglib> element. These
nested elements are described in the steps below. Where an element
uses other nested elements, the description of the nested elements is
indented with a blue line.
To create a tag library descriptor:
- Create a text file with an appropriate name and the extension
.tld, and locate it according
to the following instructions:
- If you are deploying your servlets from a Web Application, place
the TLD in the WEB-INF
directory. For more details on creating a Web Application, see the
developers guide, Writing a Web
Application.
- If you are deploying your JSP pages using the weblogic.properties file (in the default servlet
context), create a directory called WEB-INF under the documentRoot, and place the TLD file in the WEB-INF directory. Content beneath
the WEB-INF directory is
non-public and is not served over HTTP by WebLogic Server.
- Include the following header:
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"web-jsptaglib_1_1.dtd">
- Add the contents of the TLD, embedded in a <taglib> element. The contents include
elements containing information about the tag library and elements
that define each tag. For example:
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>quote</shortname>
<info>
This tag library contains several tag extensions
useful for formatting content for HTML.
</info>
<tag>
<name>code</name>
<tagclass>weblogic.taglib.quote.CodeTag</tagclass>
<bodycontent>tagdependent</bodycontent>
<attribute>
<name>fontAttributes</name>
</attribute>
<attribute>
<name>commentColor</name>
</attribute>
<attribute>
<name>quoteColor</name>
</attribute>
</tag>
</taglib>
This example declares a new tag called code. The tag's functionality is implemented by the
Java class weblogic.taglib.quote.CodeTag. The tag can take three
optional attributes. The next steps describe the XML elements in
the TLD used for declaring a tag library.
- Identify the Tag Library
The following elements identify the tag library, and are
nested within the
<taglib> ... </taglib> element:
- <tlibversion>version_number</tlibversion>
- (Required) The version number of the tag library. The
current version number is 1.0.
- <jspversion>version_number</jspversion>
- (Optional) The JSP version that this tag library is designed to
work with. WebLogic supports JSP version 1.1 from this release.
- <shortname>TagLibraryName</shortname>
- (Required) Assigns a short name to this tag library. This
element is not used by WebLogic Server.
- <uri>unique_string</uri>
- (Optional)This element is not used by WebLogic Server.
- <info>...text...</info>
- (Optional) Use this element to provide a description of the
tag library. This element is not used by WebLogic Server.
- Define a Tag
Use a separate <tag>
element to define each new tag in the tag library. The <tag> element takes the
following nested tags:
<tag>
 |
 |
 |
- <name>tag_name</name>
- (Required) Defines the name of the tag. This is used when
using the tag in a JSP file, after the ":" symbol, For example:
<mytaglib:tag_name>
For more information, see Using Custom Tags in a
JavaServer Page.
- <tagclass>package.class.name</tagclass>
- (Required) Declares the tag handler class that implements the
functionality of this tag. Specify the fully qualified package name
of the class.
If you are deploying your JSPs from a Web Application, Locate the
class files under the WEB-INF/classes directory, under a directory structure
reflecting the package name. If you are deploying your JSPs in the
default servlet context using the weblogic.properties file, locate the class files
somewhere in the servlet
classpath.
- <teiclass>package.class.name</teiclass>
- (Optional) Declares the subclass of TagExtraInfo that describes the scripting
variables introduced by this tag. If your tag does not define new
scripting variables, it does not use this element. Specify the fully
qualified package name of the class.
If you are deploying your JSPs from a Web Application, Locate the
class files under the WEB-INF/classes directory, under a directory structure
reflecting the package name. If you are deploying your JSPs in the
default servlet context using the weblogic.properties file, locate the class files
somewhere in the servlet
classpath.
- <bodycontent>tagdependent | JSP |
empty</bodycontent>
- (Optional) Defines the content of the tag body. If you
specify empty, then use the
tag in the empty tag format <taglib:tagname\> in the JSP page. If you specify
JSP, the contents of the tag
can be interpreted as JSP and you must use the tag in the body
tag format <taglib:tagname>. Specify tagdependent if your tag will interpret the
contents of the body as non-JSP (for instance an SQL statement).
If the <bodycontent> element
is not defined, the default value is JSP.
- <attribute>
 |
 |
 |
Use a separate <attribute> element to define each attribute that
the tag can take. Tag attributes allow the JSP author to alter the
behavior of your tags.
- <name>myAttribute</name>
- (Required) Defines the name of the attribute as it appears in the
tag element in the JSP page. For example:
<taglib:mytag myAttribute="myAttributeValue">
- <required>true | false</required>
- (Optional) Defines whether this attribute has optional use
in the JSP page. If not defined here, the default is false -- that is,
the attribute is optional by default. If true is specified, and the attribute is not used in a
JSP page, a translation-time error occurs.
- <rtexprvalue>true |
false</rtexprvalue>
- (Optional) Defines whether this attribute can take a
scriptlet expression as a value, allowing it to be dynamically
calculated at request time.
The default value false is assumed
if this element is not specified.
|
- </attribute>
|
- </tag>
IV. Implementing the tag handler
This section describes how to write Java classes that implement the
functionality of an extended tag.
Tag handler API
The JSP1.1 API defines a set of classes and interfaces that you
use to write custom tag handlers. Documentation for the javax.servlet.jsp.tagext API is
currently available at http://java.sun.com/j2ee/j2sdkee/techdocs/api/index.html.
Your tag handler must implement one of two interfaces:
- Tag
- Implement the Tag interface if your custom tag is an empty-body
tag. The API also provides a convenience class TagSupport that implements the Tag interface and provides default empty methods
for the methods defined in the interface.
- BodyTag
- Implement the BodyTag interface if your custom tag needs to use a
body. The API also provides a convenience class BodyTagSupport that implements the BodyTag interface and provides
default empty methods for the methods defined in the
interface. Because BodyTag
extends Tag it is a superset
of the interface methods.
Tag Handler Lifecycle
The methods inherited from either the Tag or BodyTag
interfaces and implemented by the tag handler class are invoked by the
JSP engine at specific points during the processing of the JSP
page. These methods signify points in the tag's lifecycle and are
executed in the following sequence:
- When the JSP engine encounters a tag in a translated JSP page,
a new tag handler is initialized. The setPageContext() and setParent() methods are invoked to set up the
environment context for the tag handler. As a tag developer, you need
not implement these methods if you extend the TagSupport or BodyTagSupport base classes.
- The setXXXX() JavaBean-like
methods for each tag attribute are invoked. For more details, see
Using Tag Attributes.
- The doStartTag()
method is invoked. You can define this method in your tag handler
class to initialize your tag handler or open connections to any
resources it needs, such as a database.
At the end of the method, you can determine if the tag body should
be evaluated or not by returning one of the following value constants
from your tag handler class:
- SKIP_BODY
- Directs the JSP engine to skip the body of the tag. Return this
value if the tag is an empty-body tag. The body-related parts of the
tag's lifecycle are skipped, and the next method invoked is
doEndTag().
- EVAL_BODY_INCLUDE
- Directs the JSP engine to evaluate and include the content of the
tag body. The body-related parts of the tag's lifecycle are skipped,
and the next method invoked is doEndTag().
You can only return this value for tags that simply implement the
Tag interface. This allows you
to write a tag that can determine whether its body is included, but is
not concerned with the contents of the body. You cannot return this
value if your tag implements the BodyTag interface (or extends the BodyTagSuport class).
- EVAL_BODY_TAG
- Instructs the JSP engine to evaluate the tag body, then invokes the
doInitBody() method.
You can only return this value if your tag implements the
BodyTag interface (or extends the
BodyTagSupport class).
- The setBodyContent() method
is invoked. At this point, any output
from the tag is diverted into a special JspWriter
called BodyContent, and is not sent to
the client.
All content from evaluating the body is appended to the
BodyContent buffer.
This method allows the tag handler to store a reference to the
BodyContent buffer so it is
available to the doAfterBody()
method for post-evaluation processing.
If the tag is passing output out to the JSP page (or the
surrounding tag scope if it is nested), the tag must explicitly write
its output to the parent-scoped JspWriter between this point in the tag lifecycle and
the end of the doEndTag()
method. The tag handler can gain access to the enclosing output using
the getEnclosingWriter()
method.
You need not implement this method if you are using the BodyTagSupport convenience class,
because it keeps a reference to the BodyContent and makes it available through the getBodyContent() method.
- The doInitBody() method is invoked. This method allows you
to perform some work immediately before the tag body is evaluated
for the first time. You might use this opportunity to set up some
scripting variables, or to push some content into the BodyContent before the tag body. The
content you prepend here will not be evaluated as JSP -- unlike
the tag body content from the JSP page.
The significant difference between performing work in this method
and performing work at the end of the doStartTag() method (once you know you are going to
return EVAL_BODY_TAG) is that
with this method, the scope of the tag's output is nested and does not
go directly to the JSP page (or parent tag). All output is now
buffered in a special type of JspWriter called BodyContent.
- The doAfterBody()
method is invoked. This method is called after the body of the tag is
evaluated and appended to the BodyContent buffer. Your tag handler should implement
this method to perform some work based on the evaluated tag body. If
your handler extends the convenience class BodyTagSupport, you can use the getBodyContent() method to access the evaluated
body. If you are simply implementing the BodyTag interface, you should have defined the setBodyContent() method where you
stored a reference to the BodyContent instance.
At the end of the doAfterBody()
method, you can determine the lifecycle of the tag again by returning one of
the following value constants:
- SKIP_BODY
- Directs the JSP engine to continue, not evaluating the body
again. The tag's lifecycle goes to the doEndTag() method.
- EVAL_BODY_TAG
- Directs the JSP engine to evaluate the body again. The evaluated body is
appended to the BodyContent and the
doAfterBody() method is invoked again.
At this point, you may want your tag handler to write output to
the surrounding scope. Obtain a writer to the enclosing scope using
the BodyTagSupport.getPreviousOut() method or the BodyContent.getEnclosingWriter()
method. Either method obtains the same enclosing writer.
Your tag handler can write the contents of the evaluated body to
the surrounding scope, or can further process the evaluated body and
write some other output. The BodyContent is appended to the existing BodyContent upon each iteration
through the body, so only write out the entire iterated body content
once you decide you are going to return SKIP_BODY. Otherwise, you will see the content of each
subsequent iteration repeated in the output.
- The out writer in the
pageContext is restored to the
parent JspWriter. This object
is actually a stack that is manipulated by the JSP engine on the pageContext using the pushBody() and popBody() methods. However, do not attempt to
manipulate the stack using these methods in your tag handler.
- The doEndTag() method is invoked. Your tag handler can
implement this method to perform post-tag server side work, write
output to the parent scope JspWriter, or close resources such as database
connections.
Your tag handler writes output directly to the surrounding scope using the
JspWriter obtained from
pageContext.getOut() in the doEndTag() method.
The previous step restored pageContext.out
to the enclosing writer when popBody() was invoked on the pageContext.
You can control the flow for evaluation of the rest of the JSP page by
returning one of the following values from the doEngTag() method:
- EVAL_PAGE
- Directs the JSP engine to continue processing the rest of the JSP page.
- SKIP_PAGE
- Directs the JSP engine to skip the rest of the JSP page.
- The release() method is
invoked. This occurs just before the tag handler instance is dereferenced and
made available for garbage collection.
Using tag attributes
Your custom tags can define any number of attributes that can be
specified from the JSP page. You can use these attributes to pass
information to the tag handler and customize its behavior.
You declare each attribute name in the tag library
Descriptor, in the
<attribute>
element. This declares the name of the attribute and other attribute
properties.
Your tag handler must implement setter and getter methods
based on the attribute name, similar to the JavaBean convention. If you
declare an attribute named foo, your
tag handler must define the following public methods:
public void setFoo(String f);
public String getFoo();
Note that the first letter of the attribute name is capitalized after the
set/get prefix.
The JSP engine invokes the setter methods for each attribute
appropriately after the tag handler is initialized and before the doStartTag() method is called. Generally,
you should implement the setter methods to store the attribute value in a
member variable that is accessible to the other methods of the tag handler.
Defining New Scripting Variables
Your tag handler can introduce new scripting variables that can be
referenced by the JSP page at various scopes. Scripting variables can
be used like implicit objects within their defined scope.
Define a new scripting variable by using the
<teiclass> element
to identify a Java class that extends javax.servlet.jsp.tagext.TagExtraInfo. For example:
<teiclass>weblogic.taglib.session.ListTagExtraInfo</teiclass>
Then write the ExtraTagInfo
class. For example:
package weblogic.taglib.session;
import javax.servlet.jsp.tagext.*;
public class ListTagExtraInfo extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData data) {
return new VariableInfo[] {
new VariableInfo("username",
"String",
true,
VariableInfo.NESTED),
new VariableInfo("dob",
"java.util.Date",
true,
VariableInfo.NESTED)
};
}
}
This class defines a single method getVariableInfo() which returns an array of VariableInfo elements. Each element
defines a new scripting variable. The example shown above defines two
scripting variables called "username" and "dob" of type String and java.util.Date respectively.
The constructor for VariableInfo()
takes four arguments.
- A String that
defines the name of the new variable.
- A String that defines the Java
type of the variable. Give the full package name for types in packages other
than the java.lang package.
- A boolean that declares
whether the variable must be instantiated before use. Set this
argument to "true" unless your tag handler is written in a language
other than Java.
- An int declaring the scope
of the variable. Use a static field from VariableInfo shown here:
- VariableInfo.NESTED
- Available only within the start and eng tags of the tag.
- VariableInfo.AT_BEGIN
- Available from the start tag until the end of the page.
- VariableInfo.AT_END
- Available from the end tag until the end of the page.
Configure your tag handler to initialize the value of the
scripting variables via the page context. For example, the following
Java source could be used in the doStartTag() method to initialize the values of the
scripting variables defined above:
pageContext.setAttribute("name", nameStr);
pageContext.setAttribute("dob", bday);
Where the first parameter names the scripting variable, and the second
parameter is the value assigned. Here, the Java variable nameStr is of type String and bday is of
type java.util.Date.
Dynamically named scripting variables
It is possible to define the name of a new scripting variable from
a tag attribute. Doing so allows you to use multiple instances of a
tag that defines a scripting variable at the same scope, without
having that tag's scripting variable names clash. In order to achieve
this from your class that extends TagExtraInfo, you must get the name of the scripting
variable from the TagData that
is passed into the getVariableInfo() method.
From TagData, you can retrieve the
value of the attribute that names the scripting variable using the getAttributeString() method. There is also the
getId() method that returns
the value of the id attribute,
which is often used to name a new implicit object from JSP tag.
Writing Cooperative Nested Tags
You can design your tags so they implicitly use properties from
tags they are nested within. An example of this can be found in the SQL Example where a
<sql:query> tag must be
nested within a <sql:connection> tag. The query tag searches for
a parent scope connection tag and uses the JDBC connection it has
established.
To locate a parent scope tag, your nested tag uses the static
findAncestorWithClass() method of the
TagSupport class. Here is an example
taken from the QueryTag example.
try {
ConnectionTag connTag = (ConnectionTag)
findAncestorWithClass(this,
Class.forName("weblogic.taglib.sql.ConnectionTag"));
} catch(ClassNotFoundException cnfe) {
throw new JspException("Query tag connection "+
"attribute not nested "+
"within connection tag");
}
This example returns the closest parent tag class whose tag
handler class matched the class given. If the direct parent tag is not
of this type, then it is parent is checked and so on until a matching
tag is found, or a ClassNotFoundException is thrown.
Using this feature in your custom tags can simplify the syntax and usage of
tags in the JSP page.