dev@javaserverfaces.java.net

Pass-through attribute rendering - how do we optimize this?

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Fri, 31 Aug 2007 10:46:51 -0700

PROBLEM:
 
    In our current implementation, each renderer has an array of known
passthrough
    attributes it needs to handle. When the component is rendered, this
array is passed
    to RenderKitUtils.renderPassThruAttributes(). The logic here is
pretty simple, we
    iterate through all of the known attributes and call
component.getAttributes(). If we
    have a non-null return, we render the attribute.

    So as a simple example using the UIData.jsp example in the
jsf-standard demo,
    we spend about 1872 ms over 1461 invocations. Most of this time is
spent (1148ms)
    calling Method instances since most, if not all, of the pass-through
attributes on the
    Html components map to java properties.

    I think in general it would be safe to say that it's not the norm
for a component to have
    more than 6 pass-through attributes defined, so if a component has a
total of 20 pass through
    attributes, then we waste a lot of cycles when only 6 or so need to
be rendered.

--------------------------------------------------------------------------
SOLUTION 1:
    - Add a package-private List<String> to UIComponent with a method to
will create/return the
      list. This list would be stored within the UIComponent instance's
attribute map.
    - Update the implementation for UIComponent.setValueExpression() and
the attribute map
      implementation to add attribute names to this list.
    - When rendering, check for the existence of this List. If present,
then call an optimized
       version of RenderKitUtils.renderPassThroughAttributes() which
will, for each of the
       attribute names in the List, search the known pass-through
attributes for this component
       and render them.

    PROBLEMS WITH SOLUTION 1:
     - what if a developer uses a component binding where they create
the component,
       call a setter (which maps to a pass-through attribute) against
the component directly,
       and then call getAttributes().put(), or set a value expression?
This would cause the
       list to be created which would trigger the optimized rendering
path, but the value set
       via setter wouldn't be known, and thus wouldn't be rendered.
--------------------------------------------------------------------------

--------------------------------------------------------------------------
    SOLUTION 2:
        - Add a package-private List<String> to UIComponent with a
method to will create/return the
          list. This list would be stored within the UIComponent
instance's attribute map.
        - Update the implementation for UIComponent.setValueExpression()
and the attribute map
          implementation to add attribute names to this list.
        - Update the HTML component generator to:
        - for each setter that maps to a pass-through attribute, ensure
it will add the attribute name to the list.
        - for each pass-through attribute of the component that has a
default property (such a HtmlForm's enctype),
          ensure the attribute is added to the list when the component
is instantiated.

   PROBLEMS WITH SOLUTION 2:
       - The main problem with solution 1 is solved, but now, what if a
developer extends
         the default HTML component and overrides a method mapping to a
pass-through
         attribute and never calls super()? Some other causes the
attribute list to be created,
         but because how the developer overrode the method, that
attribute isn't in the list and
         therefore doesn't get rendered.
--------------------------------------------------------------------------

--------------------------------------------------------------------------
    SOLUTION 3:
      - Based on solution 2, but add a static marker to the generated
component denoting
        it's suitable to be optimized.
      - Update ApplicationImpl.createComponent() to check for this
marker and if present,
         add a flag (probably to the attributes map) that can be picked
up by the rendering
         logic to select the appropriate rendering method. This would
solve the issue
         with solution 2.

     PROBLEMS WITH SOLUTION 3:
      - Adding some overhead to the ApplicationImpl.createComponent()
code path (probably minor)
      - An application developer completely replaces the Application
object or overrides createComponent()
        so that nothing gets flagged, so rendering occurs as it does now
(back to square one).
--------------------------------------------------------------------------

Issues aside, I've implemented solution 2 locally, and have seen a very
nice improvment in
the rendering. Specifically, for a similar number of invocations, the
time rendering pass through
attributes was reduced to 384 ms.

So, I'm looking for feedback:
   - thoughts on each solution
   - solutions that haven't been considered
   - other problems that haven't been considered
   - Is the performance gain mentioned worth the additional complexity?