4 Implementing the Tag Handler
- Simple Tag Handler Life Cycle (SimpleTag Interface)
- Tag Handler Life Cycle (Tag and BodyTag Interfaces)
- Iteration Over a Body Tag (IterationTag Interface)
- Handling Exceptions within a Tag Body
- Using Tag Attributes
- Defining New Scripting Variables
- Writing Cooperative Nested Tags
- Using a Tag Library Validator
Simple Tag Handler Life Cycle (SimpleTag Interface)
Simple tag handlers present a much simpler invocation protocol than do classic tag handlers. The tag library descriptor maps tag library declarations to their physical underlying implementations. A simple tag handler is represented in Java by a class that implements the SimpleTag
interface.
The life cycle of a simple tag handler is straightforward and is not complicated by caching semantics. Once a simple tag handler is instantiated by the container, it is executed and then discarded. The same instance must not be cached and reused. Initial performance metrics show that caching a tag handler instance does not necessarily lead to greater performance. To accommodate such caching makes writing portable tag handlers difficult and makes the tag handler prone to error.
In addition to being simpler to work with, simple tag extensions do not rely directly on any servlet APIs, which allows for potential future integration with other technologies. This capability is facilitated by the JspContext
class, which Page-Context
extends. JspContext
provides generic services such as storing the JspWriter
and keeping track of scoped attributes, whereas PageContext
has functionality specific to serving JSPs in the context of servlets. Whereas the Tag interface relies on PageContext
, SimpleTag
only relies on JspContext
. The body of SimpleTag
, if present, is translated into a JSP fragment and passed to the setJspBody
method. The tag can then execute the fragment as many times as needed.
See JSP Fragments.
Unlike classic tag handlers, the SimpleTag
interface does not extend Tag
. Instead of supporting doStartTag()
and doEndTag()
, the SimpleTag
interface provides a simple doTag()
method, which is called once and only once for any given tag invocation. All tag logic, iteration, body evaluations, and so on are performed in this single method. Thus, simple tag handlers have the equivalent power of BodyTag
, but with a much simpler life cycle and interface.
To support body content, the setJspBody()
method is provided. The container invokes the setJspBody()
method with a JspFragment
object encapsulating the body of the tag. The tag handler implementation can call invoke()
on that fragment to evaluate the body. The SimpleTagSupport
convenience class provides getJsp-Body()
and other useful methods to make this even easier.
Parent topic: Implementing the Tag Handler
Events in the Simple Tag Handler Life Cycle
The following describes the life cycle of simple tag handlers, from creation to invocation. When a simple tag handler is invoked, the following events occur in order:
-
Simple tag handlers are created initially using a zero argument constructor on the corresponding implementation class. Unlike classic tag handlers, a simple tag handler instance must never be pooled by the container. A new instance must be created for each tag invocation.
-
The
setJspContext()
andsetParent()
methods are invoked on the tag handler. ThesetParent()
method need not be called if the value being passed in isnull
. In the case of tag files, aJspContext
wrapper is created so that the tag file can appear to have its own page scope. CallinggetJspContext()
must return the wrappedJsp-Context
. -
The attributes specified as XML element attributes (if any) are evaluated next, in the order in which they are declared, according to the following rules (referred to as "evaluating an XML element attribute"). The appropriate bean property setter is invoked for each. If no setter is defined for the specified attribute but the tag accepts dynamic attributes, the
setDynamicAttribute()
method is invoked as the setter.If the attribute is a scripting expression (for example,
"<%= 1+1 %>"
in JSP syntax, or"%= 1+1 %"
in XML syntax), the expression is evaluated, and the result is converted and passed to the setter.Otherwise, if the attribute contains any expression language expressions (for example,
"Hello ${name}"
), the expression is evaluated, and the result is converted and passed to the setter.Otherwise, the attribute value is taken verbatim, converted, and passed to the setter.
-
The value for each
<jsp:attribute>
element is evaluated, and the corresponding bean property setter methods are invoked for each, in the order in which they appear in the body of the tag. If no setter is defined for the specified attribute but the tag accepts dynamic attributes, thesetDynamicAttribute()
method is invoked as the setter.Otherwise, if the attribute is not of type
JspFragment
, the container evaluates the body of the<jsp:attribute>
element. This evaluation can be done in a container-specific manner. Container implementors should note that in the process of evaluating this body, other custom actions may be invoked.Otherwise, if the attribute is of type
JspFragment
, an instance of aJsp-Fragment
object is created and passed in. -
The value for the body of the tag is determined, and if a body exists the
setJsp-Body()
method is called on the tag handler.If the tag is declared to have a
body-content
of empty or no body or an empty body is passed for this invocation, thensetJspBody()
is not called.Otherwise, the body of the tag is either the body of the
<jsp:body>
element, or the body of the custom action invocation if no<jsp:body>
or<jsp:attribute>
elements are present. In this case, an instance of aJsp-Fragment
object is created and it is passed to the setter. If the tag is declared to have abody-content
oftagdependent
, theJspFragment
must echo the body's contents verbatim.Otherwise, if the tag is declared to have a
body-content
of typescriptless
, theJspFragment
must evaluate the body's contents as a JSP scriptless body. -
The
doTag()
method is invoked. -
The implementation of
doTag()
performs its function, potentially calling other tag handlers (if the tag handler is implemented as a tag file) and invoking fragments. -
The
doTag()
method returns, and the tag handler instance is discarded. IfSkip-PageException
is thrown, the rest of the page is not evaluated and the request is completed. If this request was forwarded or included from another page (or servlet), only the current page evaluation stops. -
For each tag scripting variable declared with scopes
AT_BEGIN
orAT_END
, the appropriate scripting variables and scoped attributes are declared, as with classic tag handlers.
Parent topic: Simple Tag Handler Life Cycle (SimpleTag Interface)
JSP Fragments
A JSP fragment is a portion of JSP code passed to a tag handler that can be invoked as many times as needed. You can think of a fragment as a template that is used by a tag handler to produce customized content. Thus, unlike simple attributes which are evaluated by the container, fragment attributes are evaluated by tag handlers during tag invocation.
JSP fragments can be parameterized using the JSP expression language (JSP EL) variables in the JSP code that composes the fragment. The JSP EL variables are set by the tag handler, thus allowing the handler to customize the fragment each time it is invoked.
See WebLogic JSP Reference in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.
During the simple tag handler life cycle, the body of a SimpleTag
, if present, is translated into a JSP fragment and passed to the setJspBody
method. During the translation phase, various sections of the page are translated into implementations of the javax.servlet.jsp.tagext.JspFragment
abstract class, before being passed to a tag handler. This is done automatically for any JSP code in the body of a named attribute (one that is defined by <jsp:attribute>
) that is declared to be a fragment, or of type JspFragment
, in the tag library descriptor (TLD). This is also automatically done for the body of any tag handled by a simple tag handler. Once passed in, the tag handler can then evaluate and re-evaluate the fragment as many times as needed, or even pass it along to other tag handlers, in the case of tag files.
A JSP fragment can be parameterized by a tag handler by setting page-scoped attributes in the JspContext
associated with the fragment. These attributes can then be accessed by way of the expression language.
A translation error must occur if a piece of JSP code that is to be translated into a JSP fragment contains scriptlets or scriptlet expressions.
Parent topic: Simple Tag Handler Life Cycle (SimpleTag Interface)
Tag Handler Life Cycle (Tag and BodyTag Interfaces)
The methods inherited from 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 life cycle of a tag and are executed in the following sequence:
-
When the JSP engine encounters a tag in a JSP page, a new tag handler is initialized. The
setPageContext()
andsetParent()
methods of thejavax.servlet.jsp.tagext.Tag
interface 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 theTagSupport
orBodyTagSupport
base classes. -
The
setXXXX()
JavaBean-like methods for each tag attribute are invoked. -
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
doStartTag()
method, you can determine whether the tag body should be evaluated 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 life cycle are skipped, and the next method invoked isdoEndTag()
.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 life cycle are skipped, and the next method invoked isdoEndTag()
.You can only return this value for tags that 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 theBodyTag
interface (or extends theBodyTagSuport
class).EVAL_BODY_TAG
—Instructs the JSP engine to evaluate the tag body, then invokes thedoInitBody()
method. You can only return this value if your tag implements theBodyTag
interface (or extends theBodyTagSupport
class). -
The
setBodyContent()
method is invoked. At this point, any output from the tag is diverted into a specialJspWriter
calledBodyContent
, and is not sent to the client. All content from evaluating the body is appended to theBodyContent
buffer. This method allows the tag handler to store a reference to theBodyContent
buffer so it is available to thedoAfterBody()
method for post-evaluation processing.If the tag is passing output 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 life cycle and the end of thedoEndTag()
method. The tag handler can gain access to the enclosing output using thegetEnclosingWriter()
method.You do not need to implement this method if you are using the
BodyTagSupport
convenience class, because the tag keeps a reference to theBodyContent
and makes the reference available through thegetBodyContent()
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 theBodyContent
before the tag body. The content you prepend is not being 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 returnEVAL_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 ofJspWriter
calledBodyContent
. -
The
doAfterBody()
method is invoked. This method is called after the body of the tag is evaluated and appended to theBodyContent
buffer. Your tag handler should implement this method to perform some work based on the evaluated tag body. If your handler extends the convenience classBodyTagSupport
, you can use thegetBodyContent()
method to access the evaluated body. If you are simply implementing theBodyTag
interface, you should have defined thesetBodyContent()
method where you stored a reference to theBodyContent
instance.At the end of the
doAfterBody()
method, you can determine the life cycle 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 life cycle of the tag skips to thedoEndTag()
method.EVAL_BODY_TAG
—Directs the JSP engine to evaluate the body again. The evaluated body is appended to theBodyContent
and thedoAfterBody()
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 theBodyContent.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. Because the
BodyContent
is appended to the existingBodyContent
upon each iteration through the body, you should only write out the entire iterated body content once you decide you are going to returnSKIP_BODY
. Otherwise, you will see the content of each subsequent iteration repeated in the output. -
The
out
writer in thepageContext
is restored to the parentJspWriter
. This object is actually a stack that is manipulated by the JSP engine on thepageContext
using thepushBody()
andpopBody()
methods. Do not, however, 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 scopeJspWriter
, or close resources such as database connections.Your tag handler writes output directly to the surrounding scope using the
JspWriter
obtained frompageContext.getOut()
in thedoEndTag()
method. The previous step restoredpageContext.out
to the enclosing writer whenpopBody()
was invoked on thepageContext
.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 de-referenced and made available for garbage collection.
Parent topic: Implementing the Tag Handler
Iteration Over a Body Tag (IterationTag Interface)
A tag that implements the javax.servlet.jsp.tagext.IterationTag
interface has a method available called doAfterBody()
that allows you to conditionally re-evaluate the body of the tag. If doAFterBody()
returns IterationTag.EVAL_BODY_AGAIN
the body is re-evaluated, if doAFterBody()
returns Tag.SKIP_BODY
, the body is skipped and the doEndTag()
method is called. See the Jakarta EE Javadocs for this interface.
You can download the Javadocs at http://www.oracle.com/technetwork/java/javaee/jsp/index.html
.
Parent topic: Implementing the Tag Handler
Handling Exceptions within a Tag Body
You can catch exceptions thrown from within a tag by implementing the doCatch()
and doFinally()
methods of the javax.servlet.jsp.tagext.TryCatchFinally
interface. See the Jakarta EE Javadocs for this interface.
You can download the Javadocs at http://www.oracle.com/technetwork/java/javaee/jsp/index.html
.
Parent topic: Implementing the Tag Handler
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 TLD 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. For example, if you declare an attribute named foo
, your tag handler must define the following public methods:
public void setFoo(String f); public String getFoo();
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.
Parent topic: Implementing 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 <tei-class> element to identify a Java class that extends javax.servlet.jsp.tagext.TagExtraInfo
. For example:
<tei-class>weblogic.taglib.session.ListTagExtraInfo</tei-class>
Then write the TagExtraInfo
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) }; } }
The example above 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
, which are of type java.lang.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 thejava.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 fromVariableInfo
shown here:VariableInfo.NESTED
—Available only within the start and end 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
.
You can also access variables created with the TagExtraInfo
class by referencing it the same way you access a JavaBean that was created with useBean
.
Parent topic: Implementing the Tag Handler
Dynamically Named Scripting Variables
You can define the name of a new scripting variable from a tag attribute. This definition enables you to use multiple instances of a tag that define a scripting variable at the same scope, without the scripting variables of the tag clashing. If you want 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.
Parent topic: Defining New Scripting Variables
Defining Variables in the Tag Library Descriptor
You can define variables in the TLD.
For more information, see 7.
Parent topic: Defining New Scripting Variables
Writing Cooperative Nested Tags
You can design your tags to implicitly use properties from tags they are nested within. For example, in the code example called SQL Query (see the samples/examples/jsp/tagext/sql directory of your WebLogic Server installation) a <sql:query>
tag is nested within a <sql:connection>
tag. The query tag searches for a parent scope connection tag and uses the JDBC connection established by the parent scope.
To locate a parent scope tag, your nested tag uses the static findAncestorWithClass()
method of the TagSupport
class. The following 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.
Parent topic: Implementing the Tag Handler
Using a Tag Library Validator
A Tag Library Validator is a user-written Java class that you can use to perform validation of the XML view of a JSP page. validate(String, String, PageData)
on the validator class is invoked by the JSP compiler at translation time (when the JSP is converted to a servlet) and returns a null
string if the page is validated or a string containing error information it the validation fails.
To implement a Tag Library Validator:
Parent topic: Implementing the Tag Handler