You are viewing a plain text version of this content. The canonical link for it is here.
Posted to taglibs-dev@jakarta.apache.org by Shawn Bayern <ba...@essentially.net> on 2001/05/12 15:00:50 UTC

Tag lifecycle, reprise

Prompted by Mac's comments, I conducted an informal code review and
realized quite how widespread tag-lifecycle misconceptions are.  Most tag
developers I know, including myself, have handled things incorrectly at
one point or another.  Even the excellent Tag Libraries Tutorial on Sun's
site (http://java.sun.com/products/jsp/tutorial/TagLibrariesTOC.html) has,
in its example of an iteration tag, what seems to be a bug that most
developers have made at least once:

	public void setGroup(Collection members) {
	   if(members.size() > 0)
	      iterator = members.iterator();
	}

This morning, to respond to the problem, I've compiled a short list of
guidelines for tag-handler lifecycle management which I'd like to include
in the Jakarta-Taglibs Tutorial.  Here's a first draft.  It contains
nothing original, but it attempts to target particular misconceptions and
organize the info from the 1.2 spec that's useful for tag developers.  
Any comments?

Shawn

-----

Guidelines for tag lifecycle management:

1) Properties are expected to remain consistent.  This means that:

   - no user code should call a setXXX() accessor of a tag handler
   - the tag handler's code itself should not modify properties that are
     set by a setXXX() method
   - tag handlers should not perform invocation-specific logic in a
     setXXX() method.  That is, the setting of a property should
     have no side effects.

2) Private, invocation-specific state must be managed manually.  
   Implications include that:

   - release() is not necessarily called between invocations, which
     means that tag logic should not count on private invocation-
     specific state being reset by release()
   - doEndTag() is not necessarily called at the end of every invocation
     (in cases of abnormal termination -- e.g., an exception thrown inside
     a tag's body or by one of its methods)
   - private invocation-specific state is thus best initialized in
     doStartTag()

   - doFinally() *is* always called for tag handlers that implement
     TryCatchFinally, so this method should be used if any invocation-
     specific resources need to be released
   - release() *is* always called at least once for a tag handler before
     it is garbage-collected, so this method can and should be used to
     release any long-term resources

Examples:

a) Suppose you have a tag that accepts some sort of expression that you
   need to resolve, e.g.:

	<show value="$my-expression$"/>

   "$my-expression$" should be stored by setValue() and evaluated in
   doStartTag().  For the behavior that's almost always desired, it should
   NOT be evaluated in setValue().

   Similarly, Iterators and Enumarations should not be extracted in a
   setXXX() method.  setXXX() methods are meant to be "idempotent";
   write them so that they can be called multiple times with no
   side effects (and, conversely, only once instead of multiple times
   if the argument passed is the same).

b) Private state that's kept for each invocation should typically be
   reset in doStartTag(), not in release().  If you have an iteration
   index controlling the behavior of a loop, doStartTag() is the
   appropriate method from which to initialize it.

c) If a tag needs to open a new database connection (or file, or other
   external resource) for every invocation, the tag handler should
   implement TryCatchFilnally and close the database connection (or
   other resource) in doFinally().  doEndTag() might not be called,
   and release() might not be called until long after you expect it to be.


Re: Tag lifecycle, reprise

Posted by James Strachan <ja...@yahoo.co.uk>.
Hi Shawn

From: "Shawn Bayern" <ba...@essentially.net>
> On Mon, 14 May 2001, James Strachan wrote:
>
> > Agreed. How about we put the definitive 'tag lifecycle' guidelines in
> > a document on the Jakarta Taglibs web site?
>
> I'd be in favor of that.  It could also be incorporated into the general
> tutorial on the site, but since the material is arguably more advanced,
> maybe separating it out is useful.  I'm +1 for either.

I've taken your summary and turned it into guidelines.xml and put it here:-

    src/doc/
            guidelines.xml

such that it will be created as HTML with the web site 'look and feel' by
the build process.
I've created a link on the taglibs index page to this document, below the
"Tutorial" link..

So after the next nightly build this should appear on the main taglibs site.
We might want to do the same thing to the tutorial.html as well at some
point...

James






_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com


Re: Tag lifecycle, reprise

Posted by Shawn Bayern <ba...@essentially.net>.
On Mon, 14 May 2001, James Strachan wrote:

> Agreed. How about we put the definitive 'tag lifecycle' guidelines in
> a document on the Jakarta Taglibs web site?

I'd be in favor of that.  It could also be incorporated into the general
tutorial on the site, but since the material is arguably more advanced,
maybe separating it out is useful.  I'm +1 for either.

I'm including below a revised version that takes people's comments into
account --

==============

Guidelines for tag lifecycle management:

1) Properties are expected to remain consistent.  This means that:

   - user code should avoid calling a a setter method of a tag handler
     for properties that correspond to tag attributes.  That is, if
     a tag has a "value" attribute, user code should avoid calling
     setValue() for that tag's handler.
   - the tag handler's code itself should not modify properties that are
     set by a setXXX() method
   - tag handlers should not perform invocation-specific logic in a
     setXXX() method.  That is, the setting of a property should
     have no side effects.

2) Private, invocation-specific state must be managed manually.  
   Implications include that:

   - release() is not necessarily called between invocations, which
     means that tag logic should not count on private invocation-
     specific state being reset by release()
   - doEndTag() is not necessarily called at the end of every invocation
     (in cases of abnormal termination -- e.g., an exception thrown inside
     a tag's body or by one of its methods)
   - private invocation-specific state is thus best initialized in
     doStartTag()

   - doFinally() *is* always called for tag handlers that implement
     TryCatchFinally, so this method should be used if any invocation-
     specific resources need to be released
   - release() *is* always called at least once for a tag handler before
     it is garbage-collected, so this method can and should be used to
     release any long-term resources

3) Since tags may be re-used, user code should not refer to a tag handler
   beyond its natural scope.  This implies:

   - a tag handler must not expose itself as a scripting variable if
     that variable has AT_BEGIN or AT_END scope (although NESTED scope
     would not be problematic in this case).
   - a tag handler should not set a reference to itself in the
     PageContext associated with the page for which it is invoked,
     unless expected use of such a reference would occur only during
     the invocation of the tag handler.


Examples:

a) Suppose you have a tag that accepts some sort of expression that you
   need to resolve, e.g.:

	<show value="$my-expression$"/>

   "$my-expression$" should be stored by setValue() and evaluated (i.e.,
   resolved) in doStartTag().  For the behavior that's almost always
   desired, it should NOT be evaluated in setValue().  (Validation, such
   as syntax checking, is acceptable in the setter method.)

   Similarly, Iterators and Enumerations should not be extracted in a
   setXXX() method.  setXXX() methods are meant to be "idempotent";
   write them so that they can be called multiple times with no
   side effects (and, conversely, only once instead of multiple times
   if the argument passed is the same).

b) Private state that's kept for each invocation should typically be
   reset in doStartTag(), not in release().  If you have an iteration
   index controlling the behavior of a loop, doStartTag() is the
   appropriate method from which to initialize it.

c) If a tag needs to open a new database connection (or file, or other
   external resource) for every invocation, the tag handler should
   implement TryCatchFinally and close the database connection (or
   other resource) in doFinally().  doEndTag() might not be called,
   and release() might not be called until long after you expect it to be.


Re: Tag lifecycle, reprise

Posted by James Strachan <ja...@yahoo.co.uk>.
> Nice start Shawn.

Agreed. How about we put the definitive 'tag lifecycle' guidelines in a
document on the Jakarta Taglibs web site?

James


_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com


Re: Tag lifecycle, reprise

Posted by Glenn Nielsen <gl...@voyager.apg.more.net>.
Nice start Shawn.

It reminds me that I need to review the design of some of my early taglibs.

It would be nice if the tuturial was split up between JSP 1.1 and JSP 1.2
features instead of intermixing 1.2 features such as TryCatchFinally and
doFinally with JSP 1.1 features.

One other point, if a tag creates a script varaible who's scope is
AT_BEGIN or AT_END, the object created must be a different object
from the tag handler.  Its ok for the tag handler to be the script variable
object if its scope is NESTED.

Regards,

Glenn

Shawn Bayern wrote:
> 
> Prompted by Mac's comments, I conducted an informal code review and
> realized quite how widespread tag-lifecycle misconceptions are.  Most tag
> developers I know, including myself, have handled things incorrectly at
> one point or another.  Even the excellent Tag Libraries Tutorial on Sun's
> site (http://java.sun.com/products/jsp/tutorial/TagLibrariesTOC.html) has,
> in its example of an iteration tag, what seems to be a bug that most
> developers have made at least once:
> 
>         public void setGroup(Collection members) {
>            if(members.size() > 0)
>               iterator = members.iterator();
>         }
> 
> This morning, to respond to the problem, I've compiled a short list of
> guidelines for tag-handler lifecycle management which I'd like to include
> in the Jakarta-Taglibs Tutorial.  Here's a first draft.  It contains
> nothing original, but it attempts to target particular misconceptions and
> organize the info from the 1.2 spec that's useful for tag developers.
> Any comments?
> 
> Shawn
> 
> -----
> 
> Guidelines for tag lifecycle management:
> 
> 1) Properties are expected to remain consistent.  This means that:
> 
>    - no user code should call a setXXX() accessor of a tag handler
>    - the tag handler's code itself should not modify properties that are
>      set by a setXXX() method
>    - tag handlers should not perform invocation-specific logic in a
>      setXXX() method.  That is, the setting of a property should
>      have no side effects.
> 
> 2) Private, invocation-specific state must be managed manually.
>    Implications include that:
> 
>    - release() is not necessarily called between invocations, which
>      means that tag logic should not count on private invocation-
>      specific state being reset by release()
>    - doEndTag() is not necessarily called at the end of every invocation
>      (in cases of abnormal termination -- e.g., an exception thrown inside
>      a tag's body or by one of its methods)
>    - private invocation-specific state is thus best initialized in
>      doStartTag()
> 
>    - doFinally() *is* always called for tag handlers that implement
>      TryCatchFinally, so this method should be used if any invocation-
>      specific resources need to be released
>    - release() *is* always called at least once for a tag handler before
>      it is garbage-collected, so this method can and should be used to
>      release any long-term resources
> 
> Examples:
> 
> a) Suppose you have a tag that accepts some sort of expression that you
>    need to resolve, e.g.:
> 
>         <show value="$my-expression$"/>
> 
>    "$my-expression$" should be stored by setValue() and evaluated in
>    doStartTag().  For the behavior that's almost always desired, it should
>    NOT be evaluated in setValue().
> 
>    Similarly, Iterators and Enumarations should not be extracted in a
>    setXXX() method.  setXXX() methods are meant to be "idempotent";
>    write them so that they can be called multiple times with no
>    side effects (and, conversely, only once instead of multiple times
>    if the argument passed is the same).
> 
> b) Private state that's kept for each invocation should typically be
>    reset in doStartTag(), not in release().  If you have an iteration
>    index controlling the behavior of a loop, doStartTag() is the
>    appropriate method from which to initialize it.
> 
> c) If a tag needs to open a new database connection (or file, or other
>    external resource) for every invocation, the tag handler should
>    implement TryCatchFilnally and close the database connection (or
>    other resource) in doFinally().  doEndTag() might not be called,
>    and release() might not be called until long after you expect it to be.

-- 
----------------------------------------------------------------------
Glenn Nielsen             glenn@more.net | /* Spelin donut madder    |
MOREnet System Programming               |  * if iz ina coment.      |
Missouri Research and Education Network  |  */                       |
----------------------------------------------------------------------

Re: Tag lifecycle, reprise

Posted by Eduardo Pelegri-Llopart <Ed...@Sun.COM>.
See intermixed....

"A. Keyton Weissinger" wrote:
> 
> Hi Shawn. I agree that these topics need to be addressed in the tutorial.
> 
> A couple comments...
> 
> > 1) Properties are expected to remain consistent.  This means that:
> >
> >    - no user code should call a setXXX() accessor of a tag handler
> Should this not be amended to be the following?
>         - no user code should call a setXXX() accessor corresponding to a tag
> attribute.
> It seems to me that I may want some setters on my tag that are outside the
> attribute mechanism.
> For example, if I have:
> <ora:displayContents>
>    <ora:getContents/>
> </ora:displayContents>
> It might be nice to have a setContent() method in the parent tag that does
> not affect a tag attribute.
> This needs more thought, but I think you see what I mean....


It is OK to invoke a setter method as long as:

 * It is not a setter on a property that is an attribute.
 * THe setter is part of the 'official' sematnics of the tag.

The first constraint is specified in the JSP 1.2 spec PFD2
The second needs to be clarified.  The motivation for this one is to
allow a Tag Library to indicate thta some things are an artifact of its
implementation, not part of its real advertised semantics, and thus make
is possible for aggressive "open coding" implementations, including
those that may get rid altogether of the tag handler instance.


> 
> >    - tag handlers should not perform invocation-specific logic in a
> >      setXXX() method.  That is, the setting of a property should
> >      have no side effects.
> Might add here that the spec (JSP or the JavaBean, I believe) does not
> specify an order in which the JSP container will call setters.

Left to right.

> 
> > Examples:
> >
> > a) Suppose you have a tag that accepts some sort of expression that you
> >    need to resolve, e.g.:
> >
> >       <show value="$my-expression$"/>
> >
> >    "$my-expression$" should be stored by setValue() and evaluated in
> >    doStartTag().  For the behavior that's almost always desired, it should
> >    NOT be evaluated in setValue().
> I disagree somewhat with this. If you have some runtime validation of
> attributes values occurring, you may want this to happen in the setters --
> to short circuit the process. Why wait for doStartTag()? This would require
> evaluation, but not any further processing. THoughts?

Depends on what you want the semantics to be.  Assuming you want
semantics like "<%= ... %>", the evaluation should be done at
doStartTag().  If it were done in the setter then, since the container
(in JSP 1.2 or lower) regards your "$my-expression$" as a literal
constant not as a real run-time epxression, in repeated evaluations
(like in a loop) the container may not re-evaluate the setter, which is
not what you want if you want rtexpr-like evaluation.

	- eduard/o

> 
> Everything else +1
> 
> Keyton

RE: Tag lifecycle, reprise

Posted by Shawn Bayern <ba...@essentially.net>.
Keyton,

Good thoughts.  Proves how hard it is to write tutorials. :-)

On Sat, 12 May 2001, A. Keyton Weissinger wrote:

  > > 1) Properties are expected to remain consistent.  This means that:
  > >
  > >    - no user code should call a setXXX() accessor of a tag handler
  > Should this not be amended to be the following?
  > 	- no user code should call a setXXX() accessor corresponding to a tag
  > attribute.
  > It seems to me that I may want some setters on my tag that are outside the
  > attribute mechanism.
  > For example, if I have:
  > <ora:displayContents>
  >    <ora:getContents/>
  > </ora:displayContents>
  > It might be nice to have a setContent() method in the parent tag that
  > does not affect a tag attribute.
  > This needs more thought, but I think you see what I mean....

Fair enough.  As an aside, I tend to personally avoid, in tag handlers,
using properties gratuitously for data other than attributes.  (Of course,
there are valid reasons for doing so; I just avoid it when there's no
particular reason for doing so.)

Having unrelated properties can limit opportunities for code reuse.
Suppose I want to subclass your handler for "displayContents"; I wouldn't
be able to give my new handler an attribute called "contents."  If
"contents" is a documented JavaBean property of the class and meant to be
used as such, that's fine.  If it's, instead, just an implementation
detail, then it's arguably exposing more than it should.

  > > a) Suppose you have a tag that accepts some sort of expression that you
  > >    need to resolve, e.g.:
  > >
  > > 	<show value="$my-expression$"/>
  > >
  > >    "$my-expression$" should be stored by setValue() and evaluated in
  > >    doStartTag().  For the behavior that's almost always desired, it
  > >    shoule NOT be evaluated in setValue().
  > I disagree somewhat with this. If you have some runtime validation of
  > attributes values occurring, you may want this to happen in the
  > setters -- to short circuit the process. Why wait for
  > doStartTag()? This would require evaluation, but not any further
  > processing. THoughts?

Right, the validation is okay; believe it or not, I thought I had included
something about "checking" attributes, but I guess I didn't.  The key is
that it shouldn't be *resolved* in the accessor.

In other words, checking "$my-expression$" to make sure its syntax is
valid is perfectly fine, and that check is most efficiently handled in the
setValue() method.  But storing the object to which it resolves shouldn't
(in the typical case) be done until doStartTag(), because the object to
which the expression refers might change between the time that the
property is set (perhaps far in the distant past) and the moment that the
tag handler is actually invoked.

E.g., in

	<defineAttribute scope="page" name="a" value="b"/>
	<readAttribute scope="page" name="a"/>

	<defineAttribute scope="page" name="a" value="c"/>
	<readAttribute scope="page" name="a"/>

readAttributeHandler.setA() need not be called more than once to handle
this fragment, so accessing the pageContext in the accesor will miss
changes to the object that's stored under the key 'a'.

Shawn


RE: Tag lifecycle, reprise

Posted by "A. Keyton Weissinger" <ke...@bellsouth.net>.
Hi Shawn. I agree that these topics need to be addressed in the tutorial.

A couple comments...

> 1) Properties are expected to remain consistent.  This means that:
>
>    - no user code should call a setXXX() accessor of a tag handler
Should this not be amended to be the following?
	- no user code should call a setXXX() accessor corresponding to a tag
attribute.
It seems to me that I may want some setters on my tag that are outside the
attribute mechanism.
For example, if I have:
<ora:displayContents>
   <ora:getContents/>
</ora:displayContents>
It might be nice to have a setContent() method in the parent tag that does
not affect a tag attribute.
This needs more thought, but I think you see what I mean....

>    - tag handlers should not perform invocation-specific logic in a
>      setXXX() method.  That is, the setting of a property should
>      have no side effects.
Might add here that the spec (JSP or the JavaBean, I believe) does not
specify an order in which the JSP container will call setters.

> Examples:
>
> a) Suppose you have a tag that accepts some sort of expression that you
>    need to resolve, e.g.:
>
> 	<show value="$my-expression$"/>
>
>    "$my-expression$" should be stored by setValue() and evaluated in
>    doStartTag().  For the behavior that's almost always desired, it should
>    NOT be evaluated in setValue().
I disagree somewhat with this. If you have some runtime validation of
attributes values occurring, you may want this to happen in the setters --
to short circuit the process. Why wait for doStartTag()? This would require
evaluation, but not any further processing. THoughts?


Everything else +1

Keyton