The API and invocation protocol for classic tag handlers is
necessarily somewhat complex because scriptlets and scriptlet expressions in
tag bodies can rely on surrounding context defined using scriptlets in the enclosing
page.With the introduction of the Expression Language (EL) and JSP Standard
Tag Library (JSTL), JSP page authors can now develop JSP pages that do not need
scriptlets or scriplet expressions. This also has an implication on the requirements
that classic tag handlers need to take into consideration, with many of them
being irrelevant in most cases. This allows for a definition of a tag invocation
protocol that is easier to use for many use cases. The introduction of the simple
tag extension in JSP 2.0 signalled an easier way to implement custom actions
with a lifecycle that is as easier to work with.
Outside of the fact that simple tag extensions made work easier,
they also do not directly depend upon any Servlet APIs, which meant that they
opened up a whole new space for further integration with other technologies.
This is accomplished because PageContent now extends JspContext. 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. The Tag interface relies on PageContext, whereas
SimpleTag only relies on JspContext.
Simple Tag Extensions can be written in one of two ways:
In Java, by defining a class that implements the javax.servlet.jsp.tagext.SimpleTag
interface. This class is intended for use by advanced page authors and tag
library developers who need the flexibility of the Java language in order
to write their tag handlers. The javax.servlet.jsp.tagext.SimpleTagSupport
class provides a default implementation for all methods in SimpleTag.
In JSP syntax, using tag files. With the ability to write custom actions
in JSP syntax, page authors (with no prior knowledge of Java), advanced page
authors or tag library developers ( who know Java but are producing tag libraries
that are primarily template based presentation) have been benefitted.
SimpleTag Interface
Simple tag handlers are those that implement the SimpleTag
interface. It is worth noting that this interface is provided for situations
when the flexibility of the Java language in needed in order to write the tag
handler.
The invocation protocol used by SimpleTag is simplified from the one used for
Classic tag handlers. The javax.servlet.jsp.tagext.SimpleTagSupport class provides
a default implementation for all methods in SimpleTag. The complete interface
definition is shown below:
public interface SimpleTag extends JspTag {
public void doTag()throws JspException, java.io.IOException;
public void setParent(JspTag parent);
public JspTag getParent();
public void setJspContext(JspContext pc);
public void setJspBody(JspFragment jspBody);
}
It is important to note that that the SimpleTag interface
extends directly from JspTag and doesn't extend Tag. This implies the fact that
SimpleTag doesn't have any inherent JSP/Servlet knowledge embedded within it.
Another difference that is worth noticing is that SimpleTag only has one lifecycle
method, doTag() defined as:
public void doTag()throws JspException, java.io.IOException
The doTag() method is called only once for any given tag invocation. This means
that all code (includes tag logic, iteration, or body evaluations) related to
this tag is contained in one nice and trim method. If you compare this to the
IterationTag interface, you will notice how considerably easier it is to get
the job done.
The setJspBody() method is provided to support body content.
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
getJspBody() and other useful methods to make this even easier.
Most SimpleTag handlers should extend javax.servlet.jsp.tagext.SimpleTagSupport.
This is the convenience class, similar to TagSupport or BodyTagSupport. There
are also some helpful methods included in this class that include:
public JspFragment getJspBody() which returns the body passed in by the
container via setJspBody. The JspFragment encapsulates the body of the tag.
If the JspFragment is null, it indicates that tag has a body content type
of empty.
public static final JspTag findAncestorWithClass(JspTag
from, java.lang.Class klass) which finds the instance of a given class type
that is closest to a given instance. This method uses the getParent() method
from the Tag and/or Simple Tag interfaces. This method is used for coordination
among cooperating tags. While traversing the ancestors, for every instance
of TagAdapter (used to allow collaboration between classic Tag handlers and
SimpleTag handlers) encountered, the tag handler returned by TagAdapter.getAdaptee()
is compared to klass. In a case where the tag handler matches this class,
and not its TagAdapter, is returned.
SimpleTag Syntax
The basic syntax for a SimpleTag is:
public interface SimpleTag extends JspTag
The above syntax defines an interface for Simple Tag Handlers.
Simple Tag Handlers differ from Classic Tag Handlers in that 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, etc. are to be performed in this single
method. Thus, simple tag handlers have the equivalent power of BodyTag, but
with a much simpler lifecycle 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 as many times as it needs.
It is worth noting that a SimpleTag handler must have a public no-args constructor.
Most SimpleTag handlers should extend SimpleTagSupport.
Simple Tag Handler Lifecycle
When a simple tag handler is required on a JSP, it is instantiated
by the container, it is executed, and then it is discarded. There are no complicated
caching semantics when using this interface since nothing is cached or reused.
It was decided by the expert group that performance gains that might be made
using caching mechanisms dramatically increased the difficulty in writing portable
tag handlers and made the handlers error prone.
If performance concerns are critical, I would suggest first implementing your
tag handler as a simple tag and then taking some performance metrics to see
if your criteria are met before embarking on the more complicated and time-consuming
path of writing a Classic handler.
The following lifecycle events take place for the simple tag handler (in the
same order):
1. A new tag handler instance is created each time the tag
is encountered by the container. This is done by calling the zero argument constructor
on the corresponding implementation class. It is important to note that a new
instance must be created for each tag invocation.
2. The setJspContext() and setParent() methods are invoked on the tag handler.
If the value being passed is 'null', then the setParent() method need not be
called. In the case of tag files, a JspContext wrapper is created so that the
tag file can appear to have its own page scope. Calling getJspContext() must
return the wrapped JspContext..
3. The container calls the setters for each attribute defined for this tag in
the order in which they appear in the JSP page or Tag File. If the attribute
value is an expression language expression or a runtime expression, it gets
evaluated first and is then passed on to the setter. On the other hand if the
attribute is a dynamic-attribute then setDynamicAttribute() is called.
4. The setJspBody() method is called by the container to set the body of this
tag, as a JspFragment. A value of null is passed to setJspBody() if the tag
is declared to have a <body-content> of empty.
5. The doTag() method is called by the container. All tag logic, iteration,
body evaluations,etc. occur in this method.
6. All variables are synchronized after the doTag() method returns.