Writing JSP Extensions
- I. Introduction
- How it all fits together
- Using custom tags in a JavaServer page
- II. Creating a Tag Library Descriptor
- Tag Library Descriptor format
- Identifying the tag library
- Defining a tag
- III. Implementing a Tag Handler
- Tag handler API
- Tag handler lifecycle
- Using tag attributes
- Defining new scripting variables
- Writing cooperative nested tags
- Other related documents
- The JSP Homepage
- Using WebLogic HTTP servlets
- JSP examples
- JSP tag extension examples
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. Custom JSP tag extensions can be used in JSP pages, and can
be authored graphically in new web development tools that support JSP.
The WebLogic Server fully supports the tag extension mechanism described
in the JSP 1.1 specification at
The JSP Homepage.
How it all fits together
The functionality of a custom JSP tag is written in a Java class called
the tag handler. The tag handler class must implement one of two interfaces
that define methods which are invoked at certain points in the tag's lifecycle
to perform work. Alternatively, the JSP API provides convenient base classes
that may be extended, relieving the tag handler from having to
implement all methods in the interfaces and providing some other
convenience functionality.
One or more custom JSP tags are described by a Tag
Library, defined by a Tag Library Descriptor
(.tld) file. This
describes the each tag's syntax and ties it to the Java classes
that implement its functionality.
The Tag Library is referenced in the JSP page that uses the custom tags
via the <%@ taglib %> directive.
This directive gives the JSPServlet the information it needs to find and
handle the custom tags in the JSP page.
Using custom tags in a JavaServer page
Referencing a tag library
To use a custom tag library from a JSP page, you must reference it from a
<%@ taglib %> directive. Here is
an example:
<%@ taglib uri="/myco/taglib" prefix="mytaglib" %>
The JSP engine will attempt to find the Tag Library Descriptor
file from the
uri attribute. This 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, you will have to
specify the filename of the Tag Library Descriptor in the uri.
The prefix attribute assigns a
label to the tag library that is used in the custom tags that use this library
in the rest of this JSP page. For example, if the library from the example
above defines a new tag called newtag,
we reference it in the JSP page as:
<mytaglib:newtag>
Using custom tags
A custom tag's format can be empty (an empty-tag),
or it may contain a body (a body-tag). Both
types of tag can accept a number of attributes that are made available to the
Java implementation of 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's 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. Here is an example:
<mylib:tagA>
<h2>This is the body of tagA</h2>
You have seen this text <mylib:counter /> times!
<p>
<mylib:repeater repeat=4>
<p>Hello World!
</mylib:repeater>
</mylib:tagA>
The example above uses three custom tags (not defined here) to illustrate
the ability to nest tags within a body-tag.
The body-tag <mylib:tagA>
only sees the HTML output from its evaluated body. That is, the nested JSP
tags <mylib:counter> and
<mylib:repeater> are first
evaluated and their output becomes part of the evaluated body of the
<mylib: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. Note that 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?
Here is a summary of the types of tasks you can achieve with custom tags:
- Custom tags can produce output. A tag's output is sent to the
surrounding scope. This means:
- 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.
- Tags can 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.
- Tags can iterate over their body content until some condition is met.
This can be used to create repetitive output, or repeatedly invoke some server
side action.
- Tags can determine if the rest of the JSP page should be processed as
part of the request, or if it should be skipped.
Some example scenarios
Here are some possibilities you can achieve with custom tags:
- An empty tag may perform some server-side work based on its attributes.
The action that the tag performs may determine whether the rest of the page is
interpreted, or some other action is taken such as a redirect. This may
be useful for checking that a user is logged in before accessing a page, otherwise
redirecting them to a login page.
- An empty tag might insert content into a page based on its attributes. This
could be used to implement a simple page-hits counter or other 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. This might be used to create a
reference to an EJB, that is queried for details in the rest of the page.
- A body-tag has the option to process its evaluated body (after it has
been evaluated as JSP into HTML), then determine the output. This might be
used to produce 'quoted HTML', reformated content, or used as a parameter to
some other 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 some condition is met.
This might be used to retrieve results from a database and format each item
according to the JSP of the body.
II. 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 TLD is used by the JSP engine and authoring tools to gain
information about the extensions.
Create a text file with an appropriate name and the extension
.tld, and place it in a location 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 standard weblogic.properties file (in the default
servlet context), create a directory called WEB-INF under the documentRoot,
and place the TLD file under the
WEB-INF directory. All content beneith
the WEB-INF directory is non-public
and will not be server over HTTP by the WebLogic Server.
The TLD is written in XML notation and requires the following header:
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"web-jsptaglib_1_1.dtd">
The contents of the TLD are embedded in a <taglib> element as shown in this minimal example:
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>quote</shortname>
<info>
This tag library contains several tag extensions
useful for formating 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 section describes all of the XML
elements in the TLD used for declaring a tag library.
Tag Library Descriptor format
This section describes the syntax for a Tag Library Descriptor, as
specified in the DTD available at:
http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd
In this release, you must order the elements in the Tag Library
Descriptor file as they are defined in the DTD. This ordering is reflected in
the order of elements described below. The XML parser will throw an exception
if your TLD elements are placed out-of-order.
As mentioned above, the body of the TLD is nested in a
<taglib> ... </taglib> element. The other
elements are described below. Where an element uses other nested elements, the
description of the nested elements is indented with a blue line.
Identifying the tag library
These elements are used to 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 supposrts JSP version 1.1 from this release.
- <shortname>TagLibraryName</shortname>
- (Required) Assigns a short name to this tag library. Currently, this elements
is not used by WebLogic.
- <uri>unique_string</uri>
- (Optional) Currently, this element is not used by WebLogic.
- <info>...text...</info>
- (Optional) Use this element to provide a description of the tag
library. Currently, this element is not used by WebLogic.
Defining 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 in the
referencing JSP file after the : symbol, such as:
<mytaglib:tag_name>
For more details, 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. You must specify the fully qualified package name
of the class. The class files should be located under the WEB-INF/classes directory, under a directory structure
reflecting the package name. For more detail on writing the tag handler class,
see Implementing the tag handler.
- <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.
You must specify the fully qualified package name
of the class. The class files should be located under the WEB-INF/classes directory, under a directory structure
reflecting the package name. For more detail on writing the tag handler class,
see Defining new scripting variables.
- <bodycontent>tagdependent | JSP | empty</bodycontent>
- (Optional) Defines the content of the tag body. If you specify
empty,
then the tag must appear 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 the tag must be used in the
body-tag format <taglib:tagname>. You should specify
tagdependent if your tag expects to
interpret the contents of the body as non-JSP, for example 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 are useful for
allowing the JSP author to alter the behavior of your tags.
- <name>attr_name</name>
- (Required) Defines the name of the attribute as it appears in the
tag element in the JSP page. Such as:
<taglib:mytag attr_name="xxxxxx">
- <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 will occur.
- <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>
III. Implementing the tag handler
This section describes how to write the 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 allow you to
write custom tag handlers. Documentation for the
javax.servlet.jsp.tagext API is currently
available at the
JSP homepage. You can download the javadoc zip file and install it on your
local file system.
Your Tag handler must implement one of two interfaces:
- Tag
- You implement the Tag interface
if your custom tag is an empty-body tag. The API also provides a convenience
class TagSupport that extends
Tag and implements default methods
for its interface methods.
- BodyTag
- You implement the BodyTag interface
if your custom tag needs to use a body. The API also provides a convenience
class BodyTagSupport that extends
BodyTag and implements default methods
for its interface methods. Since BodyTag extends Tag it
is a superset of the interface methods.
Tag handler lifecycle
The interface methods of the tag handler are invoked by the JSP engine at
specific points during the processing of the JSP page. These signify points in
the tag's lifecycle and are listed below.
- When the tag is encountered in the 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 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:
- SKIP_BODY
- Directs the JSP engine to skip the body of the tag. You should 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 to be invoked is the doEndTag() method.
- EVAL_BODY_INCLUDE
- Directs the JSP engine to evaluate and include the content of the tag
body. The next method in the tag's lifecycle to be invoked is the
doEndTag() method.
You may only return this value for tags that simply implement the
Tag interface. This allows you to
write a tag that can determine if its body is included or not, 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
- This will instruct the JSP engine to evaluate the tag body, then invoke 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 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 wishes to push 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, since it keeps a reference to
the BodyContent and makes it available
through the getBodyContent() method.
- The doInitBody() method is
invoked. This method gives your tag a chance to prepare just before the tag
body is evaluated for the first time. You might use this opportunity to setup
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 significance 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 the scope of the tag's output has been 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 has been 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 next step.
- 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, your tag handler may wish to write output to the
surrounding scope.
You 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 may write the contents of the evaluated body to the
surrounding scope, or may further process the evaluated body and write some
other output. Note that the BodyContent is appended to throughout the body iterations, so
you should 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 is actually a stack
that is manipulated by the JSP engine on the pageContext using the pushBody() and popBody() methods. However, you should 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 this 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 may define any number of attributes that can be specified
from the JSP page. These can be used to pass information to the tag handler
and customize its behavior.
You declare each attribute name in the Tag Library Descriptor an 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.
Your tag informs the JSP engine that a new scripting variable is defined
in the Tag Library Descriptor using the
<teiclass> element to
identify a Java class that extends
javax.servlet.jsp.tagext.ExtraTagInfo. Here is an example:
<teiclass>weblogic.taglib.session.ListTagExtraInfo</teiclass>
You must provide the ExtraTagInfo
class. Here is an example listing:
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
that the java.lang package.
- A boolean that declares if the
variable must be instantiated before use. This is always true for Java, and is dependent of the programming
language used to implement the tag handler.
- 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.
Your tag handler must initialize the value of the scripting variables via
the page context. The following Java source might be used in the doStartTag() method to initialize the values
of the scripting variables defines 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's possible to define the name of a new scripting variable from a tag
attribute. This 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 a
convenience mehtod getId() 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 will return 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.
IV. Troubleshooting
This section contains tips on debugging your custom tag libraries. You can
expect to find more details here as we identify common issues that are
encountered by developers of custom tag libraries.
Worthy of note is the keepgenerated flag of the JSPServlet. This can be used to
inspect the generated Java code from your JSP files. For more details, see the
JSP Developers Guide section