Gang -
Ed and I spoke about this yesterday. Ed has come up with a solution
that will allow us to restore ComponentHandler to the public API
without also having to pull the entire implementation into the API.
The solution leverages a new TagHandlerHelper contract - the public
ComponentHandler class will delegate all of the thorny work to a
TagHandlerHelper. The actual TagHandlerHelper implementation will
live in the JSF implementation.
I think that this is a good compromise that balances the needs for a
ComponentHandler base class with the desire to keep the API as free
of implementation as possible. Thanks very much Ed for getting this
solution in even at this late date!
Last night I took a closer look at the new public ComponentHandler
API (in the Mojarra trunk) and also at Trinidad/ADF Faces
ComponentHandler (and ComponentSupport) usage. I found a few diffs
between the legacy Facelets APIs and the JSF 2.0 APIs that I wanted
to share with the EG.
1. ComponentSupport.isNew()
This API was present in legacy Facelets but as far as I can tell is
no longer present in JSF 2.0. Custom handler implementations use
this method to determine whether a component is newly created. If
the component is newly created, the handler might do some work to
further initialize the component. On the other hand, if the
component is not newly created, the handler might instead short-
circuit and not muck with the component.
This is a fairly common use case (shows up in both Trinidad and ADF
Faces), so I think we need something here - if not a public API,
then at least a recommended alternative. Note that
ComponentSupport.isNew() actually has a trivial implementation:
> public final static boolean isNew(UIComponent component) {
> return component != null && component.getParent() == null;
> }
So one option would be to replace calls to ComponentSupport.isNew()
with code like the above. Though of course that does mean pushing
some assumptions about component tree wiring out into user code.
2. ComponentHandler.applyNextHandler()
The legacy Facelets ComponentHandler contract included the following
API:
> protected void applyNextHandler(FaceletContext ctx, UIComponent c)
Though this is not present on our new JSF 2.0 ComponentHandler class.
ComponentHandler implementations typically override this if they
need to do pre/post-processing before/after the child tags are
applied.
I think we should be able to add this back into the JSF 2.0
ComponentHandler fairly easily. Just need the
TagHandlerHelper.apply() implementation to call back into the
ComponentHandler to apply the next handler. (I think we are going
to need to do this anyway, since the ComponentHandler has the
reference to the next handler.)
3. TagHandler.findNextByType()
The legacy Facelets TagHandler contract included the following API:
> protected final Iterator findNextByType(Class type)
It appears this method is no longer present in the JSF 2.0 API.
Custom TagHandler implementations need access to this API in cases
where they need to perform special processing of their children.
ADF Faces has a number of cases where we need to do this, so I would
like to see if we can restore this API.
Note that this method is not specific to ComponentHandler - non-
component TagHandlers may need this as well.
4. TextHandler interface
The typical way to to support TagHandlers (or ComponentHandlers) of
the form:
<foo>some text here</foo>
Is to use findNextByType() in conjunction with the TextHandler
interface.
For example, the VerbatimHandler does this:
> StringBuffer content = new StringBuffer();
> Iterator iter =
> TagHandlerImpl.findNextByType(this.nextHandler,
> TextHandler.class);
> while (iter.hasNext()) {
> TextHandler text = (TextHandler) iter.next();
> content.append(text.getText(ctx));
> }
Without having access to the TextHandler interface, it is not
possible to implement handlers that process child text.
Fortunately TextHandler is just a trivial interface with two methods:
- public String getText();
- public String getText(FaceletContext ctx);
And this interface is already present in the JSF 2.0 - but off in
the ri. Can we promote this out of the ri and into the API so that
we can enable support for tags that contain text? I don't see any
way to support this use case without access to the TextHandler
interface.
5. FacetHandler, AttributeHandler
Okay, these two are probably fairly ADF Faces-specific... ADF Faces
has its own page template component. Usage looks something like this:
> <af:pageTemplate template="path/to/some/template/file">
> <f:attribute name="foo" value="bar">
> <f:facet name="foobar">
> <h:outputText value="Hello, World!"/>
> </f:facet>
> </af:pageTemplate>
In the ComponentHandler subclass for <af:pageTemplate>, we perform
some pre-processing of attributes/facets before we process the
template file. In order to do this, we call findNextByType() twice
- once with AttributeHandler.class to find all of the attributes,
and then a second time with FacetHandler.class to grab all of the
facets.
This works beautifully in legacy Facelets, where FacetHandler/
AttributeHandler are public classes, but not so well in JSF 2.0,
since these classes are no longer part of the public API.
I understand the desire not to include these handler implementation
classes in the public API - ie. I understand why we want to keep as
much implementation as possible in the ri and not in the API.
However, assuming we cannot expose the existing FacetHandler/
AttributeHandler classes in the API... Is there any chance that we
can add marker interfaces to the public API? (Doesn't have
necessarily have to be a marker interface - an FacetHandler
interface that returns the facet name would be tremendously useful
for us.) If we can have some way to find child attributes/facets,
this would allow us to port our af:pageTemplate component to JSF
2.0. Since this is a key component for us - used throughout Oracle
products - being able to support will definitely help Oracle's
ability to upgrade to JSF 2.0.
Oh, of course, if we are willing to attempt to get these last minute
additions in, in the interest of time I would be more than happy to
provide patches/changebundles/spec prose for these APIs!
Andy
Andy Schwartz wrote On 2/27/2009 7:39 PM ET:
> Gang -
>
> I realize that it is way beyond late for me to be sending this
> email, but I think that this issue is significant enough that it is
> worth raising even at this late point.
>
> Last year when we discussed which Facelets classes should be
> included in the JSF 2.0 API, one of the questions was whether
> ComponentHandler (and ComponentConfig/Support) should be included
> in the public API. At the time I asked that these be included as
> Trinidad makes use of these API (or, in the case of
> ComponentSupport, parts of these APIs). However, Ed was reluctant
> to add these to the JSF 2.0 public API. I believe the reasoning was:
>
> 1. Our new composite components functionality should dramatically
> reduce the # of cases where component authors need to write custom
> tag handlers.
> 2. In cases where custom tag handlers are necessary, custom
> component author can implement their own handler solution, perhaps
> by extending TagHandler instead of ComponentHandler.
>
> While #1 might be an option for many custom components, it is not
> really an option for our existing Trinidad (and ADF Faces)
> components. These are Java-based components/APIs with Java-based
> Renderers - in many cases very complex Java Renderers. Composite
> components are not a solution here.
>
> Unfortunately I did not have time to take a closer look a #2 until
> very recently. On closer examination, it seems to me that re-
> implementing the functionality provided by ComponentHandler is non-
> trivial - and possibly even impossible to do in a portable way (ie.
> without depending on JSF implementation details).
>
> The non-trivial part to re-implementing a ComponentHandler is
> providing a replacement for the component tree wiring logic. This
> includes behavior such as:
>
> - Searching the parent component to see whether an existing
> component is present. (ComponentSupport.findChildByTagId())
> - Marking existing components for deletion.
> (ComponentSupport.markForDeletion())
> - Marking component instances as being newly created.
> (ComponentSupport.MARK_CREATED)
> - Cleaning up orphaned components.
> (ComponentSupport.finalizeForDeletion())
> - Creating the implicit wrapper component for facets with multiple
> children. (ComponentSupport.addComponent())
>
> Also, when adding newly created components into the component tree,
> there is some internal communication between FacetHandler and
> ComponentHandler regarding where the newly created component should
> be inserted (as indicated by FacetHandler.KEY). However, this
> communication is currently implementation-specific - a replacement
> ComponentHandler implementation cannot participate in this wiring
> without depending on internal behavior.
>
> Yikes! This is a very large burden to place on custom component
> authors. Comparing this to JSP land, this is basically the
> equivalent of asking custom component authors to re-implement
> UIComponentClassicTagBase - clearly something that we would never
> expect component authors to be required to do.
>
> Looking at the original documentation for Facelets, it seems clear
> that ComponentHandler is considered to be part of the public API:
>
>
https://facelets.dev.java.net/nonav/docs/dev/docbook.html#dev-meta-
> component
>
> Similarly, the Apress "Myfaces and Facelets" book treats
> ComponentHandler as a public API and includes a section devoted to
> writing custom ComponentHandlers.
>
> I am concerned that by excluding this ComponentHandler the JSF 2.0
> public API, we are taking away a long-standing Facelets public API,
> and in the process, making it significantly more difficult for
> component authors to upgrade existing components to JSF 2.0,
> something we clearly want to avoid (ie. we want to keep the
> migration path as smooth as possible to encourage upgrading).
>
> I am of course particularly concerned about the impact that this
> might have on the ability to upgrade Trinidad/ADF Faces to JSF 2.0
> (selfish, yes). But my concerns are not limited to Trinidad/ADF
> Faces... I did some poking around and I see that other component
> sets have the same dependency on ComponentHandler. For example,
> RichFaces has some handlers that extend ComponentHandler:
>
>
http://www.jboss.org/file-access/default/members/jbossrichfaces/freezone/...
>
> As does Tomahawk:
>
>
http://myfaces.apache.org/tomahawk-project/tomahawk12/apidocs/org/apache/...
>
> I suspect that any custom component that needs to perform
> specialized attribute processing - including components which
> support MethodExpression-based attributes - is going to need to
> extend (or, at the moment, re-implement) ComponentHandler. Rather
> than potentially placing obstacles in the way of upgrading these
> components to 2.0, I would like to ask that we re-think our
> decision to not include ComponentHandler in the JSF 2.0 public API.
>
> For the sake of completeness, just wanted to add... Here at Oracle
> we recently started an effort to move our ADF Faces JSP tags over
> to Facelets. As part of this effort we found a few other Facelets
> APIs that would be tremendously helpful to our cause, but are not
> currently in the JSF 2.0 public API. These include:
>
> - com.sun.facelets.tag.TagDecorator
> - com.sun.facelets.tag.jsf.core.AttributeHandler
> - com.sun.facelets.tag.jsf.core.FacetHandler
>
> I can of course explain the use case for these in more detail, but
> seems like it would be best to save that for another email.
>
> So, thoughts on ComponentHandler? Is there any way that we can
> possibly get this into the 2.0 API before we ship?
>
> Again, I do appreciate how late it is to be raising this (again).
> I am sorry about the last minute nature of this request.
>
> Andy