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