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?