[jsr-314-open] Behavior API overview

Kito Mann kito.mann at VIRTUA.COM
Wed Mar 11 21:08:56 EDT 2009


I agree with Martin here (thanks for the great writeup, Andy). It's weird to
have all these "Behavior" features that just focus on dealing with a script.
Andy, your proposal seems like the simplest way to handle this for now.
However, if we assume that a behavior has a single purpose, couldn't we just
rename "getScript" to "getContent" or something?
---
Kito D. Mann -- Author, JavaServer Faces in Action
http://twitter.com/kito99  http://twitter.com/jsfcentral
http://www.virtua.com - JSF/Java EE consulting, training, and mentoring
http://www.JSFCentral.com - JavaServer Faces FAQ, news, and info
+1 203-404-4848 x3





On Wed, Mar 11, 2009 at 11:06 AM, Martin Marinschek
<mmarinschek at apache.org>wrote:

> Hi Andy,
>
> ok, I see. Well, I just wanted to make sure that we don't pollute the
> namespace: again, behavior seems to generic a name to me to have a
> method providing a script. Should we have a behavior interface, an
> extending script-behavior interface, and then we can make behavior
> (the base-interface) more powerful in 2.1?
>
>
> regards,
>
> Martin
>
> On 3/10/09, Andy Schwartz <andy.schwartz at oracle.com> wrote:
> > Hey Martin -
> >
> > Thanks for reading through my long email and for the comments!
> >
> > We ended up with the getScript() and decode() methods out of necessity -
> > these were the minimal APIs that we needed to encapsulate our
> > Ajax-related functionality into a Behavior object.  As currently
> > designed, our Behavior solution is definitely focused on creating and
> > attaching scripts to components - thus we ended up with getScript() as
> > the centerpiece of the Behavior contract.
> >
> > I find your idea of Behaviors as component aspects very interesting, and
> > can see how this would add another degree of flexibility.  However, I am
> > a little uncomfortable expanding the scope at this point in the 2.0
> > release.   For 2.1, I do think it would be worth considering whether we
> > can address this, perhaps by adding a new "PhaseBehavior" interface.
> > The idea is that this would identify Behaviors that want to be notified
> > before/after each phase, similar to a PhaseListener, but at the
> > component instance-level rather than at the view-level.
> >
> > Do you feel that this is something that we can evaluate for 2.1?  Or do
> > you feel that it is critical to address this in 2.0?
> >
> > Andy
> >
> > Martin Marinschek wrote On 3/10/2009 3:02 PM ET:
> >> Hi Andy,
> >>
> >> I think this all reads very well - however, I was wondering why you
> >> stopped at providing decode and renderScript (especially the
> >> get-script method feels a little awkward in something which sounds as
> >> generic as a behaviour class).
> >>
> >> For me, this whole behavior thing sounds like a component aspect:
> >> something which is called before/after/around component lifecycle
> >> methods are executed... at least before/after/around decode and
> >> before/after/around rendering.
> >>
> >> regards,
> >>
> >> Martin
> >>
> >> On 3/10/09, Andy Schwartz <andy.schwartz at oracle.com> wrote:
> >>
> >>> Gang -
> >>>
> >>> I wanted to send one last email on the new Behavior API to help folks
> >>> who were not able to keep up with the progress on this API as it
> >>> evolved.  Much of this information has been covered in previous emails,
> >>> but some of the API details are new, so please read on if you are
> >>> interested.
> >>>
> >>> * Core APIs
> >>>
> >>> Just to remind everyone why we started down this path...  As of the
> >>> public review draft, we had a single non-generic "behavior" - the
> >>> AjaxBeahvior - which could only be attached to EditableValueHolders and
> >>> ActionSource components, and even then, only for valueChange/action
> >>> events.  So, two obvious limitations that we decided to address:
> >>>
> >>> 1. We wanted to allow other types of "behaviors" to be provided - not
> >>> limit the solution to AjaxBehavior.
> >>> 2. We wanted to allow components other than
> >>> EditableValueHolders/ActionSources to host "behaviors".
> >>>
> >>> In order to address #1, we introduced the generic
> >>> javax.faces.component.behavior.Behavior class.  The class provides the
> >>> base contract for all behavior implementations.  Note that early on we
> >>> decided that a "behavior" was something distinct from a "component".
> >>> Behaviors, like converters/validators, are objects that are attached to
> >>> existing components in order to enhance the component with
> functionality
> >>> that is not provided by the component itself.
> >>>
> >>> The main responsibility of a Behavior implementation is to cough up a
> >>> script - ie. a bit of JavaScript code that can be attached to one of
> the
> >>> component's client-side event handlers (eg. attach to "onclick").  This
> >>> responsibility is implemented via the Behavior.getScript() method.
> >>>
> >>> In order to address #2, we introduced the
> >>> javax.faces.component.behavior.BehaviorHolder interface.  Components
> >>> that want to allow behaviors to be attached implement this interface.
> >>> This allows the component to communicate the valid attach points
> >>> (getEventNames()) and the default attach point (getDefaultEventName()),
> >>> provides a mechanism for registering behaviors (addBehavior()), and
> also
> >>> provides access to any registered behaviors (getBehaviors()).
> >>>
> >>> While UIComponentBase does not implement the BehaviorHolder interface
> >>> itself, it provides default implementations of the BehaviorHolder
> >>> methods in order to make it easier for subclasses to implement
> >>> BehaviorHolder.  The minimal requirement for a UIComponentBase subclass
> >>> to implement BehaviorHolder is to provide an implementation of
> >>> getEventNames() that returns a non-empty set of the event names
> >>> supported by the component.
> >>>
> >>> These two contracts, Behavior and BehaviorHolder, form the foundation
> of
> >>> our new component behavior solution.  Hopefully the above should be
> >>> familiar since it has been discussed the number of times on the EG list
> >>> and was also covered in Appendix C of the recent spec drafts.   The
> >>> actual set of APIs that we need has grown over the last few months, so
> I
> >>> wanted to also discuss some of the more recent additions...
> >>>
> >>> * BehaviorRenderer
> >>>
> >>> Once we got the core Behavior/BehaviorHolder contract in place, we
> >>> (Roger, Ted, Alex and I) quickly realized that additional flexibility
> >>> was needed.  In particular, we felt that the scripts that are produced
> >>> by Behaivor implementations may need to vary based on the RenderKit.
>  In
> >>> order to support RenderKit-specific Behavior script generation, we
> >>> followed the existing UIComonent/Renderer pattern and introduced a
> >>> BehaviorRenderer API.  BehaviorRenderers are similar in spirit to
> >>> component Renderers, but provide a contract that is geared towards
> >>> Behaviors.  So, while component Renderers are designed for HTML content
> >>> generation and write directly to the ResponseWriter, BehaviorRenderers
> >>> simply cough up a script - ie. the main API on BehaviorRenderer is
> >>> getScript() (mirrors Behavior.getScript()).
> >>>
> >>> Of course, as with component Renderers, BehaviorRenderers are entirely
> >>> optional.  A Behavior implementation does not need to use a
> >>> BehaviorRenderer at all - it can simply override Behavior.getScript()
> >>> and produce the script locally.
> >>>
> >>> * Behavior Event Handling
> >>>
> >>> The next enhancement that we tackled was the ability for Behaviors to
> >>> participate in decoding.  This was necessary in order to support a very
> >>> important use case: the ability for Behavior implementations to deliver
> >>> events to registered listeners.  The main case that we had in mind was
> >>> AjaxBehavior, ie. we wanted to make it possible to do this:
> >>>
> >>>   <h:commandButton>
> >>>       <f:ajax event="mouseover"
> >>> listener="#{bean.doSomethingInResponseToMouseOver}"/>
> >>>   </h:commandButton>
> >>>
> >>> In order to support this we added the following APIs:
> >>>
> >>> - Behavior.decode()
> >>> - BehaviorRenderer.decode()
> >>> - Behavior.broadcast()
> >>> - BehaviorEvent (extends FacesEvent)
> >>> - BehaviorListener (extends FacesListener)
> >>>
> >>> We also specified the mechanism/conditions under which BehaviorHolder
> >>> components must give attached Behaviors the opportunity to decode.
> >>>
> >>> * BehaviorBase
> >>>
> >>> Around this time we also introduced a BehaviorBase base class that
> >>> provides base functionality for Behavior implementations (such as
> >>> listener registration, event broadcast support, BehaviorRenderer
> >>> delegation wiring).  BehaviorBase is to Behavior what UIComponentBase
> is
> >>> to UIComponent.
> >>>
> >>> * Behavior.Parameter
> >>>
> >>> As we started integrating the new behavior contract with our existing
> >>> renderers, we realized that Behavior/BehaviorRenderer getScript()
> >>> implementations may need access to various pieces of information to
> help
> >>> them generate the correct scripts.  For example, in the h:commandButton
> >>> case, the component may have f:params attached.  These f:param
> >>> names/values must be included in the request, whether the request is a
> >>> full page postback provided by the component, or an Ajax postback
> >>> provided by the AjaxBehavior.  In order to support this case, we added
> a
> >>> trivial static inner Parameter class to Behavior.  This allows
> >>> components/renderers to pass (resolved) parameter name/values into
> >>> Behavior.getScript().
> >>>
> >>> * BehaviorContext
> >>>
> >>> At this point we realized that there is danger in adding more arguments
> >>> to the Behavior.BehaviorRenderer.getScript() methods.  The problem is
> >>> that we may not be able to foresee all of the possible information that
> >>> getScript() implementations may need access to.  Rather than risk
> having
> >>> to later define new overrides of getScript(), we decided to introduce a
> >>> BehaviorContext class.  The BehaviorContext is used to pass information
> >>> (FacesContext, component, parameters, source id) into
> >>> Behavior.getScript().  If we find that we need to pass additional
> >>> information into getScript() in a later release, we can do so by adding
> >>> new methods to the BehaviorContext.
> >>>
> >>> BehaviorContext is defined as an abstract class, but provides access to
> >>> a implementation via the BehaviorContext.createBehaviorContext()
> factory
> >>> method.  Note that we explicitly avoid going through the FactoryFinder
> >>> mechanism here since this is a trivial data transfer object - and one
> >>> which may need to be instantiated many times during a typical request -
> >>> so we want to keep the overhead low.
> >>>
> >>> I keep going back and forth as to whether Behavior.decode() should also
> >>> take a BehaviorContext.  Currently it just takes a FacesContext and the
> >>> UIComponent.  I am tempted to pass in the BehaviorContext instead,
> >>> though certain information on the BehaviorContext (like the parameters)
> >>> are clearly specific to script generation.
> >>>
> >>> * BehaviorHint
> >>>
> >>> Another issue that arose while integrating Behavior support into
> >>> h:commandButton is that the component renderer may itself need to vary
> >>> its content depending on whether or not it has a Behavior attached, and
> >>> in particular depending on whether that Behavior provides a script that
> >>> performs a postback.  The issue here is that certain renderers - eg.
> >>> commandButton/Link renderers - may generate their own postback scripts
> >>> (in Mojarra these renderers generate calls to mojarra.jsfcljs()).
> >>> However, when an AjaxBehavior is attached, it provides its own postback
> >>> script that should replace/suppress the postback script that the
> >>> component renderer normally produces.  Since not all Behavior
> >>> implementations actually perform postbacks (AjaxBehavior does, but most
> >>> won't), the component renderer needs some way to determine whether or
> >>> not an attached Behavior is going to perform a postback.
> >>>
> >>> In order to solve this problem, we added a new enum, BehaviorHint, and
> a
> >>> new method to Behavior: Behavior.getHints().  getHints() returns a
> >>> Set<BehaviorHint> (implemented under the covers as an EnumSet of
> course)
> >>> that provides information about the Behavior that the consuming
> >>> component/renderer may be interested in.
> >>>
> >>> At the moment, the BehaviorHint enum only defines a single value -
> >>> BehaviorHint.SUBMITTING.  However, we chose to use an enum rather than
> a
> >>> boolean (ie. rather than a Behavior.isSubmitting() method) to allow for
> >>> additional hints in the future.  If people feel that it is too awkward
> >>> to have a single-valued enum in our API, please let me know.  We can
> >>> consider reverting to a boolean method, though an enum is more future
> >>> proof.
> >>>
> >>> * Behavior configuration
> >>>
> >>> In order to provide complete support for configuring Behavior and
> >>> BehaviorRenderer implementations, we had to touch a number of places,
> >>> including:
> >>>
> >>> - javax.faces.application.Application now supports methods for adding
> >>> and creating Behavior instances
> >>> - faces-config.xml now supports <behavior> and <behavior-renderer>
> >>> elements
> >>> - We have added annotations for identifying Behavior and
> >>> BehaviorRenderer implementations so that faces-config entries are not
> >>> required
> >>> - The Facelets taglib file now supports <behavior> tags.
> >>>
> >>> In all cases we followed the precedents already set by
> >>> converter/validator.
> >>>
> >>> * Standard HTML component BehaviorHolders
> >>>
> >>> Finally, once we had agreement our generic Behavior solution, we also
> >>> decided that it was silly not to enhance the standard HTML component
> set
> >>> to take advantage of this.  Most of the standard HTML components now
> >>> implement BehaviorHolder and thus allow Behaviors to be attached.  The
> >>> attach points for the standard HTML components correspond to the
> >>> standard "on<Event>" attributes.  So, whereas before it was only
> >>> possible to attach behaviors to EditableValueHolder/ActionSource
> >>> components, it is now possible to attach Behaviors to other standard
> >>> components, eg. <h:panelGroup>:
> >>>
> >>>   <h:panelGroup>
> >>>       <f:ajax event="mouseover" listener="#{bean.foo}"/>
> >>>   </h:panelGroup>
> >>>
> >>> This gives us significantly more flexibility - and a much more powerful
> >>> solution than we had outlined in our original plans.
> >>>
> >>> One quick note on event names... The event names/attach points do not
> >>> include the "on" prefix that is used in the component attribute names.
> >>> So:
> >>>
> >>>       <f:ajax event="mouseover"/>
> >>>
> >>> Not:
> >>>
> >>>       <f:ajax event="onMouseover"/>
> >>>
> >>> The standard ActionSource components still support a logical "action"
> >>> event/attach point.  And the standard EditableValueHolder components
> >>> still support a logical "valueChange" event/attach point.  This allows
> >>> page authors to attach Behaviors to these components without having to
> >>> be aware of the unerlying DOM event that triggers the action/value
> >>> change.  Also note that the "action" and "valueChange" events are
> >>> defined as default events for these components, which makes it possible
> >>> to do omit the event attribute, eg:
> >>>
> >>>    <!-- Fires Ajax requests in response to actions -->
> >>>    <h:commandButton>
> >>>       <f:ajax/>
> >>>   </h:commandButton>
> >>>
> >>>   <!-- Fires Ajax requests in response to value changes -->
> >>>   <h:inputText>
> >>>       <f:ajax/>
> >>>   </h:inputText>
> >>>
> >>> BTW, Jeffrey Stephenson (JSR-276) and I have been having some offline
> >>> discussions on the possibility of getting event name/default event name
> >>> metadata in place.  This would clearly help the development tool
> >>> experience.
> >>>
> >>> * Limitations
> >>>
> >>> While I think that the Behavior API is a great addition to JSF, there
> >>> are a few limitations that we have not been able to address for 2.0.
> >>> These include:
> >>>
> >>> 1. Custom renderer wiring
> >>>
> >>> Implementing the BehaviorHolder contract is trivial.  However, actually
> >>> rendering the Behavior scripts - possibly multiple scripts which may
> >>> need to be chained with other component/renderer-defined scripts - can
> >>> be tedious.  To help simplify this, we have introduced a new utility
> >>> method into our JavaScript API: jsf.util.chain().  This method takes an
> >>> arbitrary number of script arguments and executes each one until it
> >>> finds one that returns false, at which point it short-circuits.  While
> >>> this simplifies things somewhat, it is still nontrivial for a Renderer
> >>> author to produce the proper call to jsf.util.chain().
> >>>
> >>> If we had more time, I would recommend trying to get some utility
> >>> methods into the JSF API so that we can make it easier for custom
> >>> Renderer authors to produce the properly chained scripts.  However,
> >>> given how little have time we have left, I do not think that we have
> >>> time to get this right.  My current plan is to try to provide some open
> >>> source utilities to help Renderer authors solve this problem.  Once
> >>> we've worked out the right APIs in open source, I am hoping that we can
> >>> incorporate these back into the 2.1 spec.
> >>>
> >>> 2. Behavior attribute-property transparency
> >>>
> >>> I have been thinking that, like components, Behaviors should expose an
> >>> attribute map that provides attribute-property transparency.  That way,
> >>> BehaviorRenderers do not have to worry about the actual Behavior type -
> >>> they can simply grab attributes out of the attribute map.
> >>>
> >>> I briefly considered implementing this functionality, but after a quick
> >>> look at UIComponentBase.AttributeMap I got scared away.  I think the
> >>> right solution is to:
> >>>
> >>> - Break UIComponentBase.AttributeMap out into its own top-level public
> >>> class.
> >>> - Introduce an AttributeHolder contract
> >>> - Have UIComponentBase and BehaviorBase implement AttributeHolder and
> >>> use AttributeMap to hold their attributes.
> >>>
> >>> However, for obvious reasons I decided not to push this for 2.0.
> >>> Perhaps we can think about this for 2.1.
> >>>
> >>> Since we do not have a Behavior.getAttributes() API, at the moment,
> >>> BehaviorRenderer implementations are forced to cast to the actual
> >>> Behaivor type (eg. AjaxBehaviorRenderer casts the Behavior to
> >>> AjaxBehavior).  I am not happy about this, but willing to live with it
> >>> for now.
> >>>
> >>> 3. Behavior resources
> >>>
> >>> Behavior implementations clearly are going to want to pull in
> JavaScript
> >>> libraries so that these do not need to be pulled in explicitly by the
> >>> application developer.  However, at the moment we do not have a
> >>> solution/recommendation for this.  (Well, there might be some solution
> -
> >>> just haven't identified what it is yet.)
> >>>
> >>> This is another one that will need to wait for 2.1.
> >>>
> >>> I think that's it!  If you have any questions/comments on this new API,
> >>> please follow up here.
> >>>
> >>> I am hoping to do some blogging on all of this if I can ever find the
> >>> time. :-)
> >>>
> >>> Andy
> >>>
> >>>
> >>>
> >>
> >>
> >>
> >
> >
>
>
> --
>
> http://www.irian.at
>
> Your JSF powerhouse -
> JSF Consulting, Development and
> Courses in English and German
>
> Professional Support for Apache MyFaces
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jsr-314-open-mirror/attachments/20090311/5f0ad869/attachment.html 


More information about the jsr-314-open-mirror mailing list