resin Resin 3.0 tutorial Tag Reuse

Tag libraries must follow important conventions for initializing tag fields. The JSP specification lets a JSP container, like Resin, reuse tag handler instances for efficiency and performance. Because this optimization restricts how tags can use fields, even experienced developers will benefit from this tutorial.

The jsp page that uses the tags The taglib descriptor The class corresponding to <msg:message> The class corresponding to <msg:add>, used within the body of <msg:message>
An instance of the class that is associated with an xml-style custom JSP tag.

Resin aggressively reuses tag handler instances. Any tag with the same set of attributes is a candidate for reuse. For example, a tag <msg:message> with an attribute title="Test-1" may share the same tag handler instance as a <msg:message> with an attribute title="Test-2".

This ability for Resin to reuse instances of the tag handler is described in the JSP specification.

"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."

In this tutorial, the <msg:message> prints its id to show the instance of the object that is created.

<msg:message title="Test Message"> <msg:add text="Hello"/> <msg:add text="World"/> </msg:message> <msg:message> <msg:add text="Hello Again"/> <msg:add text="World"/> </msg:message> <msg:message title="Test Message"> <msg:add text="Goodbye"/> <msg:add text="World"/> </msg:message>

The instance created for the first <msg:message> tag, MessageTag@10000, is reused for the third <msg:message> tag. Resin can reuse the tag handler instance the attributes set for the first and third objects are the same: {title}. The second tag uses a new instance, MessageTag@100000, because it has a different attribute set: {text}.

+--------------------------------------------+ | instance: example.taglib.MessageTag@10000 | |--------------------------------------------| | Test Message | |--------------------------------------------| | Hello | | World | +--------------------------------------------+ +---------------------------------------------+ | instance: example.taglib.MessageTag@29000 | |---------------------------------------------| | default title | |---------------------------------------------| | Hello Again | | World | +---------------------------------------------+ +--------------------------------------------+ | instance: example.taglib.MessageTag@10000 | |--------------------------------------------| | Test Message | |--------------------------------------------| | Goodbye | | World | +--------------------------------------------+
  1. Handle the first <msg:message> tag:
    1. Instantiate the tag handler instance, MessageTag@9681e0
    2. Set the attributes, setTitle("Test Message")
    3. Call the doStartTag method.
    4. Process the contents
    5. Call the doEndTag method.
  2. Handle the second <msg:message> tag
  3. Handle the third <msg:message> tag:
    1. Set any attribute values which differ from the first tag. In this case, all the values are the same the same, so Resin doesn't call any setter.
    2. Call the doStartTag method.
    3. Process the contents
    4. Call the doEndTag method.
  4. At the end of the page, clean up the tag handler instances:
    1. Call release() for the first instance, MessageTag@10000
    2. Call release() for the second instance, MessageTag@29000
    3. Call release() for the <msg:add> instances.

Resin does not call release() between the first and third <msg:message> tags. It only calls release() at the end of the page. This behavior follows the JSP spec, but surprises some tag library developers.

Since tag handler instances are reused, the fields corresponding to tag attributes must be read-only. Only Resin may call the attribute setters; the tag's code must never modify the corresponding fields.

/* tag attributes * * member variables to store the value of tag attributes are treated * as read-only. Resin will set the values using setXXXX() based on * the values passed as attributes to the tag, and the code in the * class will never change the values. */ private String _attrTitle; public void setTitle(String title) { // this is the only place where _attrTitle is ever set _attrTitle = title; }

Tags must initialize internal variables in the first action method called, for example doStartTag. Because of tag instance reuse, the tag must assume that all internal variables are unset, i.e. set to unknown, bogus values. The initialization code must initialize those values.

Often in a tag it is necessary to have additional member variables other than those that hold the value of tag attributes. In this case, the only safe way to initialize the variables is in the doStartTag(), before anything else is done.

Because the tag instance will be reused, you cannot simply initialize the tag in the constructor. And, because the release() method is only called at the end of the page, the tag cannot call initialization code in the release() method. There may be many calls to and before is called.

In this tutorial, an init() method is used for initialization of these internal member variables, and a call to init() is the first thing done in doStartTag(). String _title; StringBuffer _msg; void init() { _title = _attrTitle; if (_title == null) _title = "Default Title"; _msg = new StringBuffer(); } public int doStartTag() throws JspException { // initialize internal member variables init(); return EVAL_BODY_BUFFERED; }

Tag libraries need to initialize their variables in the first tag method called. Most often, this will be in doStartTag, but some tags only implement doEndTag. Tags which only have a doEndTag may initialize in doEndTag.

Because Resin can analyze which methods are implemented by a tag and generated better code for the default TagSupport methods, it's a good idea to only implement those tag methods which are absolutely necessary.

If an attribute of a tag is not used, the corresponding setter will not be called. In many causes you will want to provide a default value for the attribute, and then the default value will be dependent on other member variables. Even though it is not always necessary, the best way to set a default value for a missing attribute is the same way the internal member variables are set in the previous example.

// read-only attribute field private String _attrTitle; // tag field initialized by init() private String _title; public void setTitle(String title) { _attrTitle = title; } public int doStartTag() throws JspException { // initialize internal member variables init(); ... // main tag processing return EVAL_BODY_BUFFERED; } protected void init() { _title = _attrTitle; if (_title == null) _title = "Default Title"; _msg = new StringBuffer(); }