dev@javaserverfaces.java.net

Re: JSR-252 Issue 21 - Provided an additional "binding" attribute for the core Converter, Listener and Validator tags does not fit as spec doc says

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Mon, 15 Oct 2007 13:48:00 -0700

Leonardo Uribe wrote:
> Hi
>
> Reading the documentation of jsf-1_2-mrel-spec.pdf, says on section
> 9.4.5, <f:converter>
>
> "..... The implementation class for this action must meet the
> following requirements:
>
> * Must extend javax.faces.webapp.ConverterJspTag.
That's a bug - it should be ConverterELTag.
>
> * The createConverter() method must:
>
> If binding is non-null, call binding.getValue() to obtain a
> reference to the
> Converter instance. If there is no exception thrown, and
> binding.getValue()
> returned a non-null object that implements
> javax.faces.convert.Converter,
> register it by calling setConverter(). If there was an
> exception thrown, rethrow
> the exception as a JspException.Use the converterId attribute
> if the converter
> instance could not be created from the binding attribute. If
> the converterId
> attribute is set, call the createConverter() method of the
> Application
> instance for this application, passing converter id specified
> by their converterId
> attribute. If the binding attribute was also set, store the
> converter instance by calling
> binding.setValue(). Register the converter instance by calling
> setConverter(). If there was an exception thrown, rethrow the
> exception as a
> JspException. "
>
> Something similar for f:validator and f:actionListener
>
> The problem is that the actual JSF RI does not do what in spec says to
> do, but JSF RI has a correct behaviour
I discussed this sometime back with the spec leads and the behavior in
the RI is preferred. The spec should
be updated (and probably will for 2.0).

Before we made this change (I think it was before 1.2_03) we received
several issues about the behavior of the
binding attribute being non-intuitive prompted the change.
>
> I have this example:
>
> bindingCLV.jsp
>
> <%@ page session="false" contentType="text/html;charset=utf-8"%>
> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
>
> <html>
> <f:view beforePhase="#{bindingCLVBean.beforePhase}"
> afterPhase="#{bindingCLVBean.afterPhase}">
> <%_at_include file="inc/head.inc"%>
> <body>
> <f:loadBundle
> basename="org.apache.myfaces.examples.resource.example_messages"
> var="example_messages" />
> <h1>Myfaces Examples JSF 1.2 Additions</h1>
> <h:messages></h:messages>
> <h:form id="form">
> <h:panelGrid id="grid" columns="3">
> <h:outputLabel value="BigInt" for="bigint"></h:outputLabel>
> <h:inputText id="bigint" binding="#{bindingCLVBean.input1}"
> value="#{bindingCLVBean.bigint}">
> <f:converter converterId="javax.faces.BigInteger"
> binding="#{bindingCLVBean.converterBigint}"/>
> <f:validator validatorId="org.apache.myfaces.bindingCLV.DummyValidator"
> binding="#{bindingCLVBean.validatorBigint}"/>
> </h:inputText>
> <h:message for="bigint"></h:message>
> <h:outputLabel value="BigDecimal" for="bigdecimal"></h:outputLabel>
> <h:inputText id="bigdecimal" binding="#{bindingCLVBean.input2}"
> value="#{bindingCLVBean.bigdecimal}">
> <f:converter converterId="javax.faces.BigDecimal"
> binding="#{bindingCLVBean.converterBigdecimal}"/>
> <f:validator validatorId="org.apache.myfaces.bindingCLV.DummyValidator"
> binding="#{bindingCLVBean.validatorBigdecimal}"/>
> </h:inputText>
> <h:message for="bigdecimal"></h:message>
> </h:panelGrid>
> <h:commandButton id="button1" value="press me"
> action="#{bindingCLVBean.update}" >
> <f:actionListener
> type="org.apache.myfaces.bindingCLV.DummyActionListener"
> binding="#{bindingCLVBean.listener}" />
> </h:commandButton>
> </h:form>
> </body>
> </f:view>
> </html>
>
> Bean:
>
> package org.apache.myfaces.bindingCLV;
>
> import java.math.BigDecimal;
> import java.math.BigInteger;
>
> import javax.faces.application.FacesMessage;
> import javax.faces.component.html.HtmlInputText;
> import javax.faces.context.FacesContext;
> import javax.faces.convert.Converter;
> import javax.faces.event.ActionListener;
> import javax.faces.event.PhaseEvent;
> import javax.faces.validator.Validator;
>
> import org.apache.commons.logging.Log;
> import org.apache.commons.logging.LogFactory;
>
> public class BindingCLVBean {
>
> private BigInteger bigint;
>
> private BigDecimal bigdecimal;
>
> private Converter converterBigint;
>
> private Converter converterBigdecimal;
>
> private Validator validatorBigint;
>
> private Validator validatorBigdecimal;
>
> private HtmlInputText input1;
>
> private HtmlInputText input2;
>
> private ActionListener listener;
>
> Log log = LogFactory.getLog(BindingCLVBean.class);
>
> public void beforePhase(PhaseEvent phaseEvent){
> FacesContext facesContext = FacesContext.getCurrentInstance();
>
> facesContext.addMessage(null, new FacesMessage("This is the message
> for phase before "+phaseEvent.getPhaseId().toString()));
> facesContext.addMessage(null, new
> FacesMessage("Component:"+this.getInput1()));
> facesContext.addMessage(null, new
> FacesMessage("Validator:"+this.getValidatorBigdecimal()));
> facesContext.addMessage(null, new
> FacesMessage("Converter:"+this.getConverterBigdecimal()));
> facesContext.addMessage(null, new
> FacesMessage("ActionListener:"+this.getListener()));
> log.info("This is the message for phase before
> "+phaseEvent.getPhaseId().toString()+" : ");
> }
>
> public void afterPhase(PhaseEvent phaseEvent){
> FacesContext facesContext = FacesContext.getCurrentInstance();
>
> facesContext.addMessage(null, new FacesMessage("This is the message
> for phase after "+phaseEvent.getPhaseId().toString()));
> facesContext.addMessage(null, new
> FacesMessage("Component:"+this.getInput1()));
> facesContext.addMessage(null, new
> FacesMessage("Validator:"+this.getValidatorBigdecimal()));
> facesContext.addMessage(null, new
> FacesMessage("Converter:"+this.getConverterBigdecimal()));
> facesContext.addMessage(null, new
> FacesMessage("ActionListener:"+this.getListener()));
> log.info("This is the message for phase after
> "+phaseEvent.getPhaseId().toString()+" : ");
> }
>
> public BigInteger getBigint() {
> return bigint;
> }
>
> public void setBigint(BigInteger bigint) {
> this.bigint = bigint;
> }
>
> public BigDecimal getBigdecimal() {
> return bigdecimal;
> }
>
> public void setBigdecimal(BigDecimal bigdecimal) {
> this.bigdecimal = bigdecimal;
> }
>
> public Converter getConverterBigint() {
> return converterBigint;
> }
>
> public void setConverterBigint(Converter converterBigint) {
> this.converterBigint = converterBigint;
> }
>
> public Converter getConverterBigdecimal() {
> return converterBigdecimal;
> }
>
> public void setConverterBigdecimal(Converter converterBigdecimal) {
> this.converterBigdecimal = converterBigdecimal;
> }
>
> public Validator getValidatorBigint() {
> return validatorBigint;
> }
>
> public void setValidatorBigint(Validator validatorBigint) {
> this.validatorBigint = validatorBigint;
> }
>
> public Validator getValidatorBigdecimal() {
> return validatorBigdecimal;
> }
>
> public void setValidatorBigdecimal(Validator validatorBigdecimal) {
> this.validatorBigdecimal = validatorBigdecimal;
> }
>
> public String update(){
> FacesContext context = FacesContext.getCurrentInstance();
>
> return "update";
> }
>
> public HtmlInputText getInput1() {
> return input1;
> }
>
> public void setInput1(HtmlInputText input1) {
> this.input1 = input1;
> }
>
> public HtmlInputText getInput2() {
> return input2;
> }
>
> public void setInput2(HtmlInputText input2) {
> this.input2 = input2;
> }
>
> public ActionListener getListener() {
> return listener;
> }
>
> public void setListener(ActionListener listener) {
> this.listener = listener;
> }
>
> }
>
> This example just work as a test to let me see what JSF RI are doing.
>
> The first time a page is called it return the following:
>
> * This is the message for phase before RENDER_RESPONSE 6
> * Component:javax.faces.component.html.HtmlInputText_at_164de59
> <mailto:javax.faces.component.html.HtmlInputText_at_164de59>
> * Validator:null
> * Converter:null
> * ActionListener:null
>
> The spec says that createConverter() should resolve the binding!!!!
> But looking on
> tld file where is defined f:converter tag, JSF RI are using another
> file to implement the tag
> called com.sun.faces.taglib.jsf_core.ConverterTag , and if this class
> implements what spec
> says the previous thing should not happen. When you submit some data
> it returns this:
>
> * This is the message for phase before APPLY_REQUEST_VALUES 2
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:null
> * Converter:null
> * ActionListener:null
> * This is the message for phase after APPLY_REQUEST_VALUES 2
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:null
> * Converter:null
> * ActionListener:null
> * This is the message for phase before PROCESS_VALIDATIONS 3
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:null
> * Converter:null
> * ActionListener:null
> * This is the message for phase after PROCESS_VALIDATIONS 3
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> * ActionListener:null
> * This is the message for phase before UPDATE_MODEL_VALUES 4
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> * ActionListener:null
> * This is the message for phase after UPDATE_MODEL_VALUES 4
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> * ActionListener:null
> * This is the message for phase before INVOKE_APPLICATION 5
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> * ActionListener:null
> * This is the message for phase after INVOKE_APPLICATION 5
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> *
> ActionListener:org.apache.myfaces.bindingCLV.DummyActionListener_at_391da0
> <mailto:org.apache.myfaces.bindingCLV.DummyActionListener_at_391da0>
> * This is the message for phase before RENDER_RESPONSE 6
> * Component:javax.faces.component.html.HtmlInputText_at_14aa2db
> <mailto:javax.faces.component.html.HtmlInputText_at_14aa2db>
> * Validator:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2
> <mailto:org.apache.myfaces.bindingCLV.DummyValidator_at_10df4e2>
> * Converter:javax.faces.convert.BigDecimalConverter_at_1485542
> <mailto:javax.faces.convert.BigDecimalConverter_at_1485542>
> *
> ActionListener:org.apache.myfaces.bindingCLV.DummyActionListener_at_391da0
> <mailto:org.apache.myfaces.bindingCLV.DummyActionListener_at_391da0>
>
> So, the binding for converter, validator and actionlistener are set
> only when they are used, which is good
> behaviour but bad comparing the jsf spec.
>
> Looking further I have found doing again a black box test on JSF RI,
> when I call getConverter() method,
> it returns something like this:
>
> converter:
> com.sun.faces.taglib.jsf_core.ConverterTag$BindingConverter_at_1406eb6
> <mailto:BindingConverter_at_1406eb6>
>
> Where is my javax.faces.convert.BigIntegerConverter
> <mailto:javax.faces.convert.BigIntegerConverter_at_168e429> instance? I
> suppose some delegate pattern inside this code that instantiate the
> converter
> validator or actionlistener the first time is used, and this delegate
> class should be used for save and restore the state on the tree, but a
> different impl as the jsf spec says.
>
> on myfaces project this issue is MYFACES-1741.
>
> I'm not an expert, and I don't know how request of changes for spec
> are submitted, so I prefer send a mail to this discussion list and let you
> decide what do you want to do.
>
> And sorry for my bad english :)
>
> regards
>
> Att: Leonardo Uribe
>
>
>
>
>
>
>
>
>
>