See: Description
Interface | Description |
---|---|
BodyTag |
The BodyTag interface extends IterationTag by defining additional methods that let a tag handler manipulate the
content of evaluating its body.
|
DynamicAttributes |
For a tag to declare that it accepts dynamic attributes, it must implement this interface.
|
IterationTag |
The IterationTag interface extends Tag by defining one additional method that controls the reevaluation of its body.
|
JspIdConsumer |
This interface indicates to the container that a tag handler wishes to be provided with a compiler generated ID.
|
JspTag |
Serves as a base class for Tag and SimpleTag.
|
SimpleTag |
Interface for defining Simple Tag Handlers.
|
Tag |
The interface of a classic tag handler that does not want to manipulate its body.
|
TryCatchFinally |
The auxiliary interface of a Tag, IterationTag or BodyTag tag handler that wants additional hooks for managing
resources.
|
Class | Description |
---|---|
BodyContent |
An encapsulation of the evaluation of the body of an action so it is available to a tag handler.
|
BodyTagSupport |
A base class for defining tag handlers implementing BodyTag.
|
FunctionInfo |
Information for a function in a Tag Library.
|
JspFragment |
Encapsulates a portion of JSP code in an object that can be invoked as many times as needed.
|
PageData |
Translation-time information on a JSP page.
|
SimpleTagSupport |
A base class for defining tag handlers implementing SimpleTag.
|
TagAdapter |
Wraps any SimpleTag and exposes it using a Tag interface.
|
TagAttributeInfo |
Information on the attributes of a Tag, available at translation time.
|
TagData |
The (translation-time only) attribute/value information for a tag instance.
|
TagExtraInfo |
Optional class provided by the tag library author to describe additional translation-time information not described
in the TLD.
|
TagFileInfo |
Tag information for a tag file in a Tag Library; This class is instantiated from the Tag Library Descriptor file
(TLD) and is available only at translation time.
|
TagInfo |
Tag information for a tag in a Tag Library; This class is instantiated from the Tag Library Descriptor file (TLD) and
is available only at translation time.
|
TagLibraryInfo |
Translation-time information associated with a taglib directive, and its underlying TLD file.
|
TagLibraryValidator |
Translation-time validator class for a JSP page.
|
TagSupport |
A base class for defining new tag handlers implementing Tag.
|
TagVariableInfo |
Variable information for a tag in a Tag Library; This class is instantiated from the Tag Library Descriptor file
(TLD) and is available only at translation time.
|
ValidationMessage |
A validation message from either TagLibraryValidator or TagExtraInfo.
|
VariableInfo |
Information on the scripting variables that are created/modified by a tag (at run-time).
|
References in this document to JSP refer to the Jakarta Server Pages unless otherwise noted.
Custom actions can be used by JSP authors and authoring tools to simplify writing JSP pages. A custom action can be either an empty or a non-empty action.
An empty tag has no body. There are two equivalent syntaxes, one with separate start and end tags, and one where the start and end tags are combined. The two following examples are identical:
<x:foo att="myObject"></foo> <x:foo att="myObject"/>
A non-empty tag has a start tag, a body, and an end tag. A prototypical example is of the form:
<x:foo att="myObject" > BODY </x:foo/>
The Jakarta Server Pages specification provides a portable mechanism for the description of tag libraries.
A JSP tag library contains
This API is described in the following sections:
JSP 2.0 introduces a new type of Tag Handler called a Simple Tag Handler, which is described in a later section. The protocol for Simple Tag handlers is much more straightforward.
A tag handler is a run-time, container-managed object that evaluates custom actions during the execution of a JSP page. A tag handler supports a protocol that allows the JSP container to provide good integration of the server-side actions within a JSP page.
A tag handler is created initially using a zero argument
constructor on its corresponding class; the method
java.beans.Beans.instantiate()
is not used.
A tag handler has some properties that are exposed to the page as attributes on an action; these properties are managed by the JSP container (via generated code). The setter methods used to set the properties are discovered using the JavaBeans introspector machinery.
The protocol supported by a tag handler provides for passing of parameters, the evaluation and reevaluation of the body of the action, and for getting access to objects and other tag handlers in the JSP page.
A tag handler instance is responsible for processing one request at a time. It is the responsability of the JSP container to enforce this.
Additional translation time information associated with the action
indicates the name of any scripting variables it may introduce, their
types and their scope. At specific moments, the JSP container will
automatically synchronize the PageContext
information with variables
in the scripting language so they can be made available directly
through the scripting elements.
A tag handler has some properties. All tag handlers have a pageContext property for the JSP page where the tag is located, and a parent property for the tag handler to the closest enclosing action. Specific tag handler classes may have additional properties.
All attributes of a custom action must be JavaBeans component properties, although some properties may not be exposed as attributes. The attributes that are visible to the JSP translator are exactly those listed in the Tag Library Descriptor (TLD).
All properties of a tag handler instance exposed as attributes will be initialized by the container using the appropriate setter methods before the instance can be used to perform the action methods. It is the responsibility of the JSP container to invoke the appropriate setter methods to initialize these properties. It is the responsability of user code, be it scriptlets, JavaBeans code, or code inside custom tags, to not invoke these setter methods, as doing otherwise would interfere with the container knowledge.
The setter methods that should be used when assigning a value to an attribute of a custom action are determined by using the JavaBeans introspector on the tag handler class, then use the setter method associated with the property that has the same name as the attribute in question. An implication (unclear in the JavaBeans specification) is that there is only one setter per property.
Unspecified attributes/properties should not be set (using a setter method).
Once properly set, all properties are expected to be persistent, so that if the JSP container ascertains that a property has already been set on a given tag handler instance, it must not set it again.
The JSP container may reuse classic tag handler instances for multiple occurrences of the corresponding custom action, in the same page or in different pages, but only if the same set of attributes are used for all occurrences. If a tag handler is used for more than one occurence, the container must reset all attributes where the values differ between the custom action occurrences. Attributes with the same value in all occurrences must not be reset. If an attribute value is set as a request-time attribute value (using a scripting or an EL expression), the container must reset the attribute between all reuses of the tag handler instance. To prevent confusion, a tag handler with an empty body must not reuse any previous tag handler with a non-empty body.
User code can access property information and access and modify tag handler internal state starting with the first action method (doStartTag) up until the last action method (doEndTag or doFinally for tag handlers implementing TryCatchFinally).
Since a tag handler is a container managed object, the container needs to maintain its references; specifically, user code should not keep references to a tag handler except between the start of the first action method (doStartTag()) and the end of the last action method (doEndTag() or doFinally() for those tags that implement TryCatchFinally).
The restrictions on references to tag handler objects and on modifying attribute properties gives the JSP container substantial freedom in effectively managing tag handler objects to achieve different goals. For example, a container may implementing different pooling strategies to minimize creation cost, or may hoist setting of properties to reduce cost when a tag handler is inside another iterative tag.
A tag handler implements an action; the JSP container must follow the type conversions described in Section 2.13.2 when assigning values to the attributes of an action.
A non-empty action has a body.
A Tag handler that does not want to process its body can implement just the Tag interface. A tag handler may not want to process its body because it is an empty tag or because the body is just to be "passed through".
The Tag interface includes methods to provide page context
information to the Tag Handler instance, methods to handle the
life-cycle of tag handlers, and two main methods for performing
actions on a tag: doStartTag()
and
doEndTag()
. The method doStartTag()
is
invoked when encountering the start tag and its return value indicates
whether the body (if there is any) should be skipped, or evaluated and
passed through to the current response stream. The method
doEndTag()
is invoked when encountering the end tag; its
return value indicates whether the rest of the page should continue to
be evaluated or not.
If an exception is encountered during the evaluation of the body of a tag, its doEndTag method will not be evaluated. See the TryCatchFinally tag for methods that are guaranteed to be evaluated.
The IterationTag interface is used to repeatedly reevaluate
the body of a custom action. The interface has one method:
doAfterBody()
which is invoked after each evaluation
of the body to determine whether to reevaluate or not.
Reevaluation is requested with the value 2, which in JSP 1.1 is defined to be BodyTag.EVAL_BODY_TAG. That constant value is still kept in JSP 1.2 (for full backwards compatibility) but, to improve clarity, a new name is also available: IterationTag.EVAL_BODY_AGAIN. To stop iterating, the returned value should be 0, which is Tag.SKIP_BODY.
The TagSupport class is a base class that can be used when implementing the Tag or IterationTag interfaces.
The evaluation of a body is delivered into a BodyContent
object. This is then made available to tag handlers that implement
the BodyTag
interface. The BodyTagSupport
class provides a useful base class to simplify writing these handlers.
If a Tag handler wants to have access to the content of its body
then it must implement the BodyTag
interface.
This interface extends IterationTag, provides two additional methods
setBodyContent(BodyContent)
and
doInitBody()
and refers to an object of type BodyContent.
A BodyContent is a subclass of JspWriter
that has a
few additional methods to convert its contents into a String, insert
the contents into another JspWriter, to get a Reader into its
contents, and to clear the contents. Its semantics also assure that
buffer size will never be exceeded.
The JSP page implementation will create a BodyContent if the doStartTag() method returns a EVAL_BODY_BUFFERED. This object will be passed to doInitBody(); then the body of the tag will be evaluated, and during that evaluation out will be bound to the BodyContent just passed to the BodyTag handler. Then doAfterBody() will be evaluated. If that method returns SKIP_BODY, no more evaluations of the body will be done; if the method returns EVAL_BODY_AGAIN, then the body will be evaluated, and doAfterBody() will be invoked again.
The content of a BodyContent instance remains available until after the invocation of its associated doEndTag() method.
A common use of the BodyContent is to extract its contents into a String and then use the String as a value for some operation. Another common use is to take its contents and push it into the out Stream that was valid when the start tag was encountered (that is available from the PageContext object passed to the handler in setPageContext).
Any tag handler can optionally extend the DynamicAttributes
interface to indicate that it supports dynamic attributes. In addition
to implementing the DynamicAttributes
interface, tag
handlers that support dynamic attributes must declare that they do so in
the Tag Library Descriptor.
The TLD is what ultimately determines whether a tag handler accepts
dynamic attributes or not. If a tag handler declares that it supports
dynamic attributes in the TLD but it does not implement the
DynamicAttributes
interface, the tag handler must be
considered invalid by the container.
If the dynamic-attributes element for a tag being invoked contains the value "true", the following requirements apply:
setDynamicAttribute()
,
passing in the namespace of the attribute (or null if the attribute
does not have a namespace or prefix), the name of the attribute without
the namespace prefix, and the final value of the attribute.java.lang.Object
. If a ValueExpression
is passed as a dynamic attribute, the default value for the expected
return type is assumed to be java.lang.Object
. If a
MethodExpression
is passed as a dynamic
attribute, the default method signature is assumed to be void
method()
.setDynamicAttribute()
method throws
JspException
, the doStartTag()
or
doTag()
method is not invoked for this tag, and the
exception must be treated in the same manner as if it came from
a regular attribute setter method.In the following example, assume attributes a and b are declared using the attribute element in the TLD, attributes d1 and d2 are not declared, and the dynamic-attributes element is set to "true". The attributes are set using the calls:
setA( "1" )
, setDynamicAttribute( null, "d1", "2" )
, setDynamicAttribute( "http://www.foo.com/jsp/taglib/mytag.tld", "d2", "3" )
, setB( "4" )
, setDynamicAttribute( null, "d3", "5" )
, andsetDynamicAttribute( "http://www.foo.com/jsp/taglib/mytag.tld", "d4", "6" )
.<jsp:root xmlns:mytag="http://www.foo.com/jsp/taglib/mytag.tld" version="2.0"> <mytag:invokeDynamic a="1" d1="2" mytag:d2="3"> <jsp:attribute name="b">4</jsp:attribute> <jsp:attribute name="d3">5</jsp:attribute> <jsp:attribute name="mytag:d4">6</jsp:attribute> </mytag:invokeDynamic> </jsp:root>
In this example, we are assuming that x:iterate is an iterative tag, while x:doit and x:foobar are simple tag. We will also assume that x:iterate and x:foobar implement the TryCatchFinally interface, while x:doit does not.
<x:iterate src="foo"> <x:doit att1="one" att2="<%= 1 + 1 %>" /> <x:foobar /> <x:doit att1="one" att2="<%= 2 + 2 %>" /> </x:iterate> <x:doit att1="one" att2="<%= 3 + 3 %>" />
The particular code shown below assumes there is some pool of tag handlers that are managed (details not described, although pool managing is simpler when there are no optional attributes), and attemps to reuse tag handlers if possible. The code also "hoists" setting of properties to reduce the cost when appropriate, e.g. inside an iteration.
boolean b1, b2; IterationTag i; // for x:iterate Tag d; // for x:doit Tag d; // for x:foobar page: // label to end of page... // initialize iteration tag i = get tag from pool or new(); i.setPageContext(pc); i.setParent(null); i.setSrc("foo"); // x:iterate implements TryCatchFinally try { if ((b1 = i.doStartTag()) == EVAL_BODY_INCLUDE) { // initialize doit tag // code has been moved out of the loop for show d = get tag from pool or new(); d.setPageContext(pc); d.setParent(i); d.setAtt1("one"); loop: while (1) do { // I'm ignoring newlines... // two invocations, fused together // first invocation of x:doit d.setAtt2(1+1); if ((b2 = d.doStartTag()) == EVAL_BODY_INCLUDE) { // nothing } else if (b2 != SKIP_BODY) { // Q? protocol error ... } if ((b2 = d.doEndTag()) == SKIP_PAGE) { break page; // be done with it. } else if (b2 != EVAL_PAGE) { // Q? protocol error } // x:foobar invocation f = get tag from pool or new(); f.setPageContext(pc); f.setParent(i); // x:foobar implements TryCatchFinally try { if ((b2 = f.doStartTag()) == EVAL_BODY_INCLUDE) { // nothing } else if (b2 != SKIP_BODY) { // Q? protocol error } if ((b2 = f.doEndTag()) == SKIP_PAGE) { break page; // be done with it. } else if (b2 != EVAL_PAGE) { // Q? protocol error } } catch (Throwable t) { f.doCatch(t); // caught, may been rethrown! } finally { f.doFinally(); } // put f back to pool // second invocation of x:doit d.setAtt2(2+2); if ((b2 = d.doStartTag()) == EVAL_BODY_INCLUDE) { // nothing } else if (b2 != SKIP_BODY) { // Q? protocol error } if ((b2 = d.doEndTag()) == SKIP_PAGE) { break page; // be done with it. } else if (b2 != EVAL_PAGE) { // Q? protocol error } if ((b2 = i.doAfterBody()) == EVAL_BODY_AGAIN) { break loop; } else if (b2 != SKIP_BODY) { // Q? protocol error } // loop } } else if (b1 != SKIP_BODY) { // Q? protocol error } // tail end of the IteratorTag ... if ((b1 = i.doEndTag()) == SKIP_PAGE) { break page; // be done with it. } else if (b1 != EVAL_PAGE) { // Q? protocol error } // third invocation // this tag handler could be reused from the previous ones. d = get tag from pool or new(); d.setPageContext(pc); d.setParent(null); d.setAtt1("one"); d.setAtt2(3+3); if ((b1 = d.doStartTag()) == EVAL_BODY_INCLUDE) { // nothing } else if (b1 != SKIP_BODY) { // Q? protocol error } if ((b1 = d.doEndTag()) == SKIP_PAGE) { break page; // be done with it. } else if (b1 != EVAL_PAGE) { // Q? protocol error } } catch (Throwable t) { i.doCatch(t); // caught, may been rethrown! } finally { i.doFinally(); }
Often two actions in a JSP page will want to cooperate, perhaps by one action creating some server-side object that needs to be accessed by another. One mechanism for doing this is by giving the object a name within the JSP page; the first action will create the object and associate the name to it while the second action will use the name to retrieve the object.
For example, in the following JSP segment the foo
action might create a server-side object and give it the
name "myObject".
Then the bar
action might access that server-side object and take some action.
<x:foo id="myObject" /> <x:bar ref="myObjet" />
In a JSP implementation, the mapping "name"->value is kept by the
implicit object
pageContext
.
This object is passed around through the Tag handler instances
so it can be used to communicate information: all it is needed
is to know the name under which the information is stored into
the pageContext.
An alternative to explicit communication of information through a named object is implicit coordination based on syntactic scoping.
For example, in the following JSP segment the foo
action might create a server-side object;
later the nested bar
action might access that server-side object.
The object is not named within the pageContext
:
it is found because the specific foo
element is the
closest enclosing instance of a known element type.
<foo> <bar/> </foo>
This functionality is supported through the
TagSupport.findAncestorWithClass(Tag, Class)
,
which uses a reference to parent tag kept by each Tag instance,
which effectively provides a run-time execution stack.
This section presents the API to implement Simple Tag Handlers. 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 which implements the
SimpleTag
interface.
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, 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. The
SimpleTagSupport
convenience class provides
getJspBody()
and other useful methods to make this even
easier.
This section describes the lifecycle of simple tag handlers, from creation to invocation. For all semantics left unspecified by this section, the semantics default to that of a classic tag handler.
When a simple tag handler is invoked, the following steps occur (in order):
setJspContext()
and setParent()
methods are invoked on the tag handler. The setParent()
method need not be called if the value being passed in is
null
. 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
.setDynamicAttribute()
method is invoked as the setter.
setDynamicAttribute()
method is invoked as the setter.
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.JspFragment
,
an instance of a JspFragment
object is created and
passed in.setJspBody()
method is called on the
tag handler.
body-content
of "empty
" or no body or an empty body is passed
for this invocation, then setJspBody()
is not
called.JspFragment
object is created as per the lifecycle
described in the JSP Fragments section and it is passed to the
setter. If the tag is declared to have a body-content
of "tagdependent
" the JspFragment
must
echo the body's contents verbatim. Otherwise, if the tag is
declared to have a body-content
of type
"scriptless
", the JspFragment
must
evaluate the body's contents as a JSP scriptless body.doTag()
method is invoked.doTag()
performs its
function, potentially calling other tag handlers (if the tag
handler is implemented as a tag file) and invoking fragments.doTag()
method returns, and the tag handler
instance is discarded. If SkipPageException
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.AT_BEGIN
or AT_END
, the appropriate
scripting variables and scoped attributes are declared, as with
classic tag handlers.JSP Fragments are represented in Java by an instance of the
javax.servlet.jsp.tagext.JspFragment abstract class
.
Pieces of JSP code are translated into JSP fragments in the context
of a tag invocation. JSP Fragments are created when providing the body of a
<jsp:attribute> standard action for an attribute that is defined
as a fragment or of type JspFragment, or when providing the body of a
tag invocation handled by a Simple Tag Handler.
Before being passed to a tag handler, the JspFragment
instance is associated with the JspContext
of the
surrounding page in an implementation-dependent manner. In addition,
it is associated with the parent Tag
or SimpleTag
instance for collaboration purposes, so that when a custom action is
invoked from within the fragment, setParent()
can be called
with the appropriate value. The fragment implementation must keep
these associations for the duration of the tag invocation in which it
is used.
The invoke()
method executes the body and directs
all output to either the passed in java.io.Writer
or
the JspWriter
returned by the getOut()
method
of the JspContext
associated with the fragment.
The implementation of each method can optionally throw a
JspException
, which must be handled by the invoker. Note
that tag library developers and page authors should not generate
JspFragment
implementations manually.
The following sections specify the creation and invocation lifecycles of a JSP Fragment in detail, from the JSP Container's perspective.
When a JSP fragment is created, the following steps occur (in order):
JspFragment
abstract class is obtained (may either be created or can optionally be cached)
each time the tag is invoked. This instance must be configured to
produce the contents of the body of the fragment when invoked. If the
fragment is defining the body of a <jsp:attribute>, the fragment
must evaluate the body each time it is invoked. Otherwise, if the
fragment is defining the body of a simple tag, the behavior of the
fragment when invoked varies depending on the body-content
declared for the tag:
body-content
is "tagdependent
",
then the fragment must echo the contents of the body verbatim
when invoked.body-content
is "scriptless
",
then the fragment must evaluate the body each time it is invoked.JspFragment
instance is passed a reference to
the current JspContext
. Whenever the fragment invokes
a tag handler, it must use this value when calling
setJspContext()
.JspFragment
instance is associated with an
instance of the tag handler of the nearest enclosing tag
invocation, or with null
if there is no enclosing tag.
Whenever the fragment invokes a tag handler, the fragment must
use this value when calling setParent()
.After a JSP fragment is created, it is passed to a tag handler for later invocation. JSP fragments can be invoked either programmatically from a tag handler written in Java, or from a tag file using the <jsp:invoke> or <jsp:doBody> standard action.
JSP fragments are passed to tag handlers using a bean property
of type JspFragment
. These fragments can be invoked by
calling the invoke()
method in the JspFragment
abstract class. Note that it is legal (and possible) for a fragment to
recursively invoke itself, indirectly.
The following steps are followed when invoking a JSP fragment:
AT_BEGIN
and
NESTED
variables in the JspContext
of the
calling page/tag, before invoking the fragment. Note that this is
not always the same as the JspContext
of the fragment
being invoked, as fragments can be passed from one tag to another.
In the case of tag files, for each variable declared
in scope AT_BEGIN
or NESTED
, if a page
scoped attribute exists with the provided name in the tag file, the
JSP container must generate code to create/update the page scoped
attribute of the provided name in the calling page/tag. If a page
scoped attribute with the provided name does not exist in the tag
file, and a page scoped attribute of the provided name is present in
the calling page, the scoped attribute is removed from the calling
page's page scope. See the chapter on Tag Files for details.var
attribute is specified,
a custom java.io.Writer
is created that can expose
the result of the invocation as a java.lang.String
object. If the varReader
attribute is
specified, a custom java.io.Writer
object is created
that can expose the resulting invocation as a
java.io.Reader
object.invoke()
method of the fragment is invoked,
passing in an optional Writer
.JspContext.getOut()
and the implicit "out" object must
be updated to send output to that writer. To accomplish
this, the container must call pushBody( writer )
on the
current JspContext
, where writer
is the
instance of java.io.Writer
passed to the fragment upon
invocation.SKIP_PAGE
, or if a Simple Tag Handler is invoked
and throws SkipPageException
, the
JspFragment
must throw SkipPageException
to
signal that the calling page is to be skipped.JspContext.getOut()
must be
restored via a call to popBody()
on the current
JspContext
.var
or varReader
attribute is specified, a scoped variable with a name equal to the
value of the var
or varReader
attribute
is created (or modified) in the page scope, and the value is set
to a java.lang.String
or java.io.Reader
respectively that can produce the results of the fragment
invocation.invoke()
method can be called again, zero or more
times. When the tag invocation defining the fragment is complete,
the tag must discard the fragment instance since it might be reused
by the container.The following non-normative example is intended to help solidify some of the concepts relating to Tag Files, JSP Fragments and Simple Tag Handlers. In the first section, two sample input files are presented, a JSP (my.jsp), and a simple tag handler implemented using a tag file (simpletag.tag). One possible output of the translation process is presented in the second section.
Although short, the example shows all concepts, including the variable directive. In practice most uses of tags will be much simpler, but probably longer.
The sample generated code is annotated with comments that point to lifecycle steps presented in various sections. The notation is as follows:
This section presents the sample source files in this scenario, from which the output files are generated.
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %> <my:simpleTag x="10"> <jsp:attribute name="y">20</jsp:attribute> <jsp:attribute name="nonfragment"> Nonfragment Template Text </jsp:attribute> <jsp:attribute name="frag"> Fragment Template Text ${var1} </jsp:attribute> <jsp:body> Body of tag that defines an AT_BEGIN scripting variable ${var1}. </jsp:body> </my:simpleTag>
<%-- /WEB-INF/tags/simpletag.tag --%> <%@ attribute name="x" %> <%@ attribute name="y" %> <%@ attribute name="nonfragment" %> <%@ attribute name="frag" fragment="true" %> <%@ variable name-given="var1" scope="AT_BEGIN" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> Some template text. <c:set var="var1" value="${x+y}"/> <jsp:invoke fragment="frag" varReader="var1"/> Invoke the body: <jsp:doBody/>
This section presents sample output files that might be generated by a JSP compiler, from the source files presented in the previous section.
public abstract class JspFragmentBase implements javax.servlet.jsp.tagext.JspFragment { protected javax.servlet.jsp.JspContext jspContext; protected javax.servlet.jsp.tagext.JspTag parentTag; public void JspFragmentBase( javax.servlet.jsp.JspContext jspContext, javax.servlet.jsp.tagext.JspTag parentTag ) { this.jspContext = jspContext; this.parentTag = parentTag; } }
// Step T.1 - Initial creation MySimpleTag _jsp_mySimpleTag = new MySimpleTag(); // Step T.2 - Set page context and parent (since parent is null, // no need to call setParent() in this case) _jsp_mySimpleTag.setJspContext( jspContext ); // Step T.3 - XML element attributes evaluated and set _jsp.mySimpleTag.setX( "10" ); // Step T.4 - <jsp:attribute> elements evaluated and set // - parameter y // (using PageContext.pushBody() is one possible implementation - // one limitation is that this code will only work for Servlet-based code). out = ((PageContext)jspContext).pushBody(); out.write( "20" ); _jsp_mySimpleTag.setY( ((javax.servlet.jsp.tagext.BodyContent)out).getString() ); out = jspContext.popBody(); // - parameter nonfragment // (using PageContext.pushBody() is one possible implementation - // one limitation is that this code will only work for Servlet-based code). // Note that trim is enabled by default, else we would have "\n Non..." out = ((PageContext)jspContext).pushBody(); out.write( "Nonfragment Template Text" ); _jsp_mySimpleTag.setNonfragment( ((javax.servlet.jsp.tagext.BodyContent)out).getString() ); out = jspContext.popBody(); // - parameter frag _jsp_mySimpleTag.setFrag( // Step C.1 - New instance of fragment created // Step C.2 - Store jspContext // Step C.3 - Association with nearest enclosing Tag instance new JspFragmentBase( jspContext, _jsp_mySimpleTag ) { public void invoke( java.io.Writer writer ) { javax.servlet.jsp.JspWriter out; // Step F.1-F.3 done in tag file (see following example) // Step F.4 - If writer provided, push body: if( out == null ) { out = this.jspContext.getOut(); } else { out = this.jspContext.pushBody( writer ); } // Step F.5 - Evaluate body of fragment: try { out.write( "Fragment Template Text " ); out.write( jspContext.getExpressionEvaluator().evaluate( "${var1}", java.lang.String.class, vResolver, fMapper, "my" ) ); } finally { // Step F.6 - Restore value of JspContext.getOut() if( writer != null ) { this.jspContext.popBody(); } } // Step F.7-F.9 done in tag file (see following example) } } ); // Step T.5 - Determine and set body of the tag // - body of tag _jsp_mySimpleTag.setJspBody( // Step C.1 - New instance of fragment created // Step C.2 - Store jspContext // Step C.3 - Association with nearest enclosing Tag instance new JspFragmentBase( jspContext, _jsp_mySimpleTag ) { public void invoke( java.io.Writer writer ) { javax.servlet.jsp.JspWriter out; // Step F.1-F.3 done in tag file (see following example) // Step F.4 - If writer provided, push body: if( writer == null ) { out = this.jspContext.getOut(); } else { out = this.jspContext.pushBody( writer ); } // Step F.5 - Evaluate body of fragment: try { out.write( "Body of tag that defines an AT_BEGIN\n" + " scripting variable " ); out.write( jspContext.getExpressionEvaluator().evaluate( "${var1}", java.lang.String.class, vResolver, fMapper, "my" ) ); out.write( ".\n" ); } finally { // Step F.6 - Restore value of JspContext.getOut() if( writer != null ) { this.jspContext.popBody(); } } // Step F.7-F.9 done in tag file (see following example) } } ); // Step T.6 - Inovke doTag // Step T.7 occurs in the tag file (see following example) // Step T.8 - doTag returns - page will catch SkipPageException. _jsp_mySimpleTag.doTag(); // Step T.9 - Declare AT_BEGIN and AT_END scripting variables String var1 = (String)jspContext.findAttribute( "var1" );
public class MySimpleTag extends javax.servlet.jsp.tagext.SimpleTagSupport { // Attributes: private String x; private String y; private String nonfragment; private javax.servlet.jsp.tagext.JspFragment frag; // Setters and getters for attributes: public void setX( Stirng x ) { this.x = x; } public String getX() { return this.x; } public void setY( String y ) { this.y = y; } public String getY() { return this.y; } public void setNonfragment( String nonfragment ) { this.nonfragment = nonfragment; } public String getNonfragment() { return this.nonfragment; } public void setFrag( javax.servlet.jsp.tagext.JspFragment frag ) { this.frag = frag; } public javax.servlet.jsp.tagext.JspFragment getFrag() { return this.frag; } protected JspContext jspContext; public void setJspContext( JspContext ctx ) { super.setJspContext( ctx ); // Step T.2 - A JspContext wrapper is created. // (Implementation of wrapper not shown). this.jspContext = new utils.JspContextWrapper( ctx ); } public JspContext getJspContext() { // Step T.2 - Calling getJspContext() must return the // wrapped JspContext. return this.jspContext; } public void doTag() throws JspException { java.lang.Object jspValue; JspContext jspContext = getJspContext(); JspContext _jsp_parentContext = SimpleTagSupport.this.getJspContext(); try { javax.servlet.jsp.JspWriter out = jspContext.getOut(); // Create page-scope attributes for each tag attribute: this.jspContext.setAttribute( "x", getX() ); this.jspContext.setAttribute( "y", getY() ); this.jspContext.setAttribute( "nonfragment", getNonfragment() ); this.jspContext.setAttribute( "frag", getFrag() ); // Synchronize AT_BEGIN variables from calling page if( (jspValue = _jsp_parentContext.getAttribute( "var1" )) != null ) { jspContext.setAttribute( "var1", value ); } else { jspContext.removeAttribute( "var1", JspContext.PAGE_SCOPE ); } // Tag template text: out.write( "\n\n\n\n\n\n\n\nSome template text.\n" ); // Invoke c:set - recognized tag handler from JSTL: jspContext.setAttribute( "var1", jspContext.getExpressionEvaluator().evaluate( "${x+y}", java.lang.String.class, jspContext, prefixMap, functionMap, "my" ) ); // Invoke the "frag" fragment: // Step F.1 - Set values of AT_BEGIN and NESTED variables // in calling page context. if( (jspValue = jspContext.getAttribute( "var1" )) != null ) { _jsp_parentContext.setAttribute( "var1", value ); } else { _jsp_parentContext.removeAttribute( "var1", JspContext.PAGE_SCOPE ); } // Step F.2 - varReader is specified, generate a writer. java.io.Writer _jsp_sout = new java.io.StringWriter(); // Step F.3 - Invoke fragment with writer getFrag().invoke( _jsp_sout ); // Step F.4 - F.6 occur in the fragment (see above) // Step F.7 - fragment returns // Step F.8 - varReader specified, so save to var jspContext.setAttribute( "var1", new StringReader( _jsp_sout.toString() ) ); // Step F.9 - Done! out.write( "\n\nInvoke the body:\n" ); // Invoke the body of the tag: // Step F.1 - Set values of AT_BEGIN and NESTED variables // in calling page context. if( (jspValue = jspContext.getAttribute( "var1" )) != null ) { _jsp_parentContext.setAttribute( "var1", value ); } else { _jsp_parentContext.removeAttribute( "var1", JspContext.PAGE_SCOPE); } // Step F.2 - varReader is not specified - does not apply. try { // Step F.3 - Invoke body, passing optional writer getJspBody().invoke( null ); } finally { // Steps F.4 - F.6 occur in the fragment (see above) // Step F.7 - fragment returns } // Step F.8 does not apply. // Step F.9 - Done! } finally { // Tag handlers generate code to synchronize AT_BEGIN with // calling page, regardless of whether an error occurs. if( (jspValue = jspContext.getAttribute( "var1" )) != null ) { _jsp_parentContext.setAttribute( "var1", value ); } else { _jsp_parentContext.removeAttribute( "var1", JspContext.PAGE_SCOPE ); } } } }
A taglib directive introduces a tag library and associates a prefix to it. The TLD associated with the library associates Tag handler classes (plus other information) with tag names. This information is used to associate a Tag class, a prefix, and a name with each custom action element appearing in a JSP page.
At execution time the implementation of a JSP page will use an available Tag instance with the appropriate property settings and then follow the protocol described by the interfaces Tag, IterationTag, BodyTag, SimpleTag, and TryCatchFinally. The implementation guarantees that all tag handler instances are initialized and all are released, but the implementation can assume that previous settings are preserved by a tag handler, to reduce run-time costs.
JSP supports scripting variables that can be declared within a
scriptlet and can be used in another. JSP actions also can be used to
define scripting variables so they can used in scripting elements, or
in other actions. This is very useful in some cases; for example, the
jsp:useBean
standard action may define an object which
can later be used through a scripting variable.
In some cases the information on scripting variables can be
described directly into the TLD using elements. A special case is typical
interpretation of the "id" attribute.
In other cases the logic that decides whether an action instance
will define a scripting variable may be quite complex and the name of
a TagExtraInfo
class is instead given in the TLD. The
getVariableInfo
method of this class is used at
translation time to obtain information on each variable that will be
created at request time when this action is executed. The method is
passed a TagData
instance that contains the
translation-time attribute values.
The TLD file contains several pieces of information that is used
to do syntactic validation at translation-time. It also contains two
extensible validation mechanisms: a TagLibraryValidator
class can be used to validate a complete JSP page, and a
TagExtraInfo
class can be used to validate a specific
action. In some cases, additional request-time validation will be done
dynamically within the methods in the Tag instance. If an error is
discovered, an instance of JspTagException
can be thrown.
If uncaught, this object will invoke the errorpage mechanism of JSP.
The TagLibraryValidator is an addition to the JSP 1.2
specification and is very open ended, being strictly more powerful
than the TagExtraInfo mechanism. A JSP page is presented via the
PageData
object, which abstracts the XML view of the JSP
page.
A PageData instance will provides an InputStream (read-only) on the page. Later specifications may add other views on the page (DOM, SAX, JDOM are all candidates), for now these views can be generated from the InputStream and perhaps can be cached for improved performance (recall the view of the page is just read-only).
As of JSP 2.0, the JSP container must support a jsp:id attribute to provide higher quality validation errors. The container will track the JSP pages as passed to the container, and will assign to each element a unique "id", which is passed as the value of the jsp:id attribute. Each XML element in the XML view will be extended with this attribute. The TagLibraryValidator can use the attribute in one or more ValidationMessage objects. The container then, in turn, can use these values to provide more precise information on the location of an error.
The prefix for the id
attribute need not be "jsp
"
but it must map to the namespace http://java.sun.com/JSP/Page
.
In the case where the user has redefined the jsp
prefix, an
alternative prefix must be used by the container.
In detail, validation is done as follows:
First, the JSP page is parsed using the information in the TLD. At this stage valid mandatory and optional attributes are checked.
Second, for each unique tag library in the page as determined by the tag library URI, and in the lexical order in which they appear, their associated validator class (if any) is invoked. This involves several substeps.
The first substep is to obtain an initialized validator instance by either:
The class name is as indicated in the <validator-class> element, and the Map passed through setInitParameters() is as described in the <init-params> element. All TagLibraryValidator classes are supposed to keep their initParameters until new ones are set, or until release() is invoked on them.
The second substep is to perform the actual validation. This is done by invoking the validate() method with a prefix, uri, and PageData that correspond to the taglib directive instance being validated and the PageData representing the page. In the case where a single URI is mapped to more than one prefix, the prefix of the first URI must be used.
The last substep is to invoke the release() method on the validator tag when it is no longer needed. This method releases all resources.
Finally, after checking all the tag library validator classes, the
TagExtraInfo classes for all tags will be consulted by invoking their
validate
method. The order of invocation of this methods
is undefined.
Comments to: jsp-dev@eclipse.org.
Copyright © 2019 Eclipse Foundation. All rights reserved.
Use is subject to license terms.