You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by "Ogrady, John " <jo...@citi.com> on 2010/01/06 20:56:12 UTC

Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the missing conversion

Java 1.5
MyFaces 1.1.7
Tomahawk 1.1.9
Spring 2.5.6
Weblogic 9.2.3


All I wanted to do was display a drop-down list of enums.  It seemed simple enough, with examples all over the 'net.  Apparently, myfaces has decided that using anything other than strings for a drop down is "no - not yours".

The setup:


My example enum:
------------------------------------------------------------
package com.facets;
import java.util.Arrays;
import java.util.List;
public enum DaysOfWeek {
    SUNDAY("Sunday"),
    MONDAY("Monday"),
    TUESDAY("Tuesday"),
    WEDNESDAY("Wednesday"),
    THURSDAY("Thursday"),
    FRIDAY("Friday"),
    SATURDAY("Saturday");

    private static List<DaysOfWeek> dayList = Arrays.asList(DaysOfWeek.values());

    private String label;

    private DaysOfWeek(String label) {
        this.label = label;
    }
    public String getLabel() {
        return this.label;
    }
    public static DaysOfWeek getByLabel(String label) {
        for(DaysOfWeek day : dayList) {
            if(day.getLabel().equals(label)) {
                return day;
            }
        }
        return null;
    }
    public static final List<DaysOfWeek> getAllList() {
        return dayList;
    }
}
------------------------------------------------------------

JSF Converter:
------------------------------------------------------------
package com.facets;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
public class DayEnumConverter implements Converter {
    public Object getAsObject(FacesContext context, UIComponent component, String value)
          throws ConverterException {
        DaysOfWeek day = DaysOfWeek.getByLabel(value);
        return (day==null ? DaysOfWeek.SUNDAY : day);
    }
    public String getAsString(FacesContext context, UIComponent component, Object object)
          throws ConverterException {
        if(object instanceof String) {
            return (String)object;
        }
        if(!(object instanceof DaysOfWeek)) {
            return "";
        }
        return ((DaysOfWeek)object).getLabel();
    }
}
------------------------------------------------------------


Spring definition to get a list of the enums:
------------------------------------------------------------
<bean id="daysEnumList"
      class="com.facets.DaysOfWeek"
      factory-method="getAllList" />
------------------------------------------------------------


A simple drop down list made from an ArrayList of Enums.
------------------------------------------------------------
<h:outputLabel for="daysOfWeekList" value="#{labels.dayList}" />
<t:selectOneMenu id="daysOfWeekList"
                 value="#{calendar.day}">
    <t:selectItems var="day"
                   value="#{daysEnumList}"
                   itemLabel="#{day.label}"
                   itemValue="#{day.label}"/>
    <f:converter converterId="dayEnumConverter"/>
</t:selectOneMenu>
------------------------------------------------------------

Assumptions:
1) I'm using SpringBeanVariableResolver to get the "daysEnumList" object from the spring application context.
2) The problem goes away if I take the daysOfWeekList selectOneMenu control off the jsp page.  Also, there is another dropdown list using only strings and that works just fine.
3) The converter is properly listed in the faces-context.xml file, as it is called normally during the JSF lifecycle - at least until the bug hits.


The Bug:
When I submit a form containing the above selectOneMenu control, the list of which is created from a Java 5 Enum, I get this error in the logs:

------------------------------------------------------------
DEBUG | 2010-01-06 13:28:53,912 | LifecycleImpl.java:178 | exiting from lifecycle.execute in RESTORE_VIEW(1) because getRenderResponse is true from one of the after listeners
------------------------------------------------------------

This means that backing bean never gets bound to the form values, and the form action is never called.  I never get past the "Apply Request Values" step of the faces lifecycle.

Take a look at this stack trace...

------------------------------------------------------------
Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended)
 _SelectItemsUtil.matchValue(Object, Iterator) line: 65
 HtmlSelectOneMenu(UISelectOne).validateValue(FacesContext, Object) line: 77
 HtmlSelectOneMenu(UIInput).validate(FacesContext) line: 428
 HtmlSelectOneMenu(UIInput).processValidators(FacesContext) line: 245
 HtmlTag(UIComponentBase).processValidators(FacesContext) line: 866
 HtmlForm(UIForm).processValidators(FacesContext) line: 78
 UIViewRoot(UIComponentBase).processValidators(FacesContext) line: 866
 UIViewRoot.processValidators(FacesContext) line: 169
 ProcessValidationsExecutor.execute(FacesContext) line: 32
 LifecycleImpl.executePhase(FacesContext, PhaseExecutor, PhaseListenerManager) line: 105
 LifecycleImpl.execute(FacesContext) line: 80
 FacesServlet.service(ServletRequest, ServletResponse) line: 143
 StubSecurityHelper$ServletServiceAction.run() line: 225
 StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest, ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet) line: 127
 ServletStubImpl.execute(ServletRequest, ServletResponse, FilterChainImpl) line: 283
 TailFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 26
 FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
 ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 246
 FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
 NavigationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 93
 DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) line: 236
 DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 167
 FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
 ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 301
 FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
 WebAppServletContext$ServletInvocationAction.run() line: 3212
 AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321
 SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject, PrivilegedAction) line: 121
 WebAppServletContext.securedExecute(HttpServletRequest, HttpServletResponse, boolean) line: 1983
 WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl) line: 1890
 ServletRequestImpl.run() line: 1344
 ExecuteThread.execute(Runnable) line: 209
 ExecuteThread.run() line: 181
------------------------------------------------------------

The bug is at line 65 of _SelectItemsUtil.

------------------------------------------------------------
44 public static boolean matchValue(Object value,
45                 Iterator selectItemsIter)
46 {
47     while (selectItemsIter.hasNext())
48     {
49         SelectItem item = (SelectItem) selectItemsIter.next();
50         if (item instanceof SelectItemGroup)
51         {
52             SelectItemGroup itemgroup = (SelectItemGroup) item;
53            SelectItem[] selectItems = itemgroup.getSelectItems();
54             if (selectItems != null
55                             && selectItems.length > 0
56                             && matchValue(value, Arrays.asList(
57                                             selectItems).iterator()))
58             {
59                 return true;
60             }
61         }
62         else
63         {
64             Object itemValue = item.getValue();
65             if (value==itemValue || (itemValue.equals(value)))
66             {
67                 return true;
68             }
69         }
70     }
71     return false;
72 }
------------------------------------------------------------


The problem is that at this point, the "value" is the value that was selected in the dropdown.  The CONVERTED value - compliments of line 428 in the UIInput class:

------------------------------------------------------------
424 Object convertedValue = getConvertedValue(context, submittedValue);
425
426 if (!isValid()) return;
427
428 validateValue(context, convertedValue);
------------------------------------------------------------

BUT - the "item" object is from an Iterator created on line 77 of UISelectOne, which only has the string values of the select component.

This means when "item.getValue()" is called on line 64 (above), you are getting the string value from whatever selectitem entry is currently targeted - and of course the object version of the selected value isn't going to equal its non-converted string value.

Either I'm missing something and there's some configuration I missed to fix this (which would make this behaviour a horrible default), or this is a pretty big bug that just cost me five hours of development time.

If no one cares about JSF 1.1 implementations anymore, just let me know and I'll not spend the time posting here.  Otherwise, any chance this could be fixed?

___________________________________________________________
John O'Grady
Dragon Tamer

Human beings, who are almost unique in having the ability to learn
from the experience of others, are also remarkable for their apparent
disinclination to do so.
- Douglas Adams

Those who do not learn from history are doomed to repeat it.
- George Santayana

Qui tacet consentit
(Silence implies consent)
___________________________________________________________


Re: Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the missing conversion

Posted by Jakob Korherr <ja...@gmail.com>.
You're welcome!

Here's the explanation to your problem:

You always have to set itemValue to an object with the same type as the
value attribute of the surrounding t:selectXXX. This is also the type you
have to write a converter for.

In your case:
<t:selectOneMenu id="daysOfWeekList"
                value="#{calendar.day}">
   <t:selectItems var="day"
                  value="#{daysEnumList}"
                  itemLabel="#{day.label}"
                  itemValue="#{day}"/>
   <f:converter converterId="dayEnumConverter"/>
</t:selectOneMenu>

#{calendar.day} references to a property of type DaysOfWeek, thus itemValue
has also to be of type DaysOfWeek and you have to write a converter, because
it is no String.

If you set itemValue="#{day.label}", you'll also have to change
#{calendar.day} to point to a String property. But that's of course not what
you want!

I hope it is now clear to you.

And sorry for my first short answer, I did not have the time to write this
earlier.

Regards,
Jakob Korherr

2010/1/6 Ogrady, John <jo...@citi.com>

>  I do so love "secret sauce" answers.  I'm not surprised that it was
> something I did (or didn't do).  This is my first foray into JSF teritory
> after years of Spring MVC, Struts, and Webwork, and faces is by far the most
> frustrating framework of all.
>
> Thanks for the quick reply!
>
> ___________________________________________________________
> John O'Grady, Dragon Tamer
> The important thing is not to stop questioning. - Albert Einstein
> ___________________________________________________________
>
>
> -----Original Message-----
> From: sethfromaustria@gmail.com [mailto:sethfromaustria@gmail.com] On
> Behalf Of Jakob Korherr
> Sent: Wednesday, January 06, 2010 3:09 PM
> To: MyFaces Discussion
> Subject: Re: Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the
> missing conversion
>
> Hi John,
>
> Use >itemValue="#{day}"< and it will work.
>
> Regards,
> Jakob Korherr
>
>
> 2010/1/6 Ogrady, John <jo...@citi.com>
>
> > Java 1.5
> > MyFaces 1.1.7
> > Tomahawk 1.1.9
> > Spring 2.5.6
> > Weblogic 9.2.3
> >
> >
> > All I wanted to do was display a drop-down list of enums.  It seemed
> > simple enough, with examples all over the 'net.  Apparently, myfaces
> > has decided that using anything other than strings for a drop down is "no
> - not yours".
> >
> > The setup:
> >
> >
> > My example enum:
> > ------------------------------------------------------------
> > package com.facets;
> > import java.util.Arrays;
> > import java.util.List;
> > public enum DaysOfWeek {
> >    SUNDAY("Sunday"),
> >    MONDAY("Monday"),
> >    TUESDAY("Tuesday"),
> >    WEDNESDAY("Wednesday"),
> >    THURSDAY("Thursday"),
> >    FRIDAY("Friday"),
> >    SATURDAY("Saturday");
> >
> >    private static List<DaysOfWeek> dayList =
> > Arrays.asList(DaysOfWeek.values());
> >
> >    private String label;
> >
> >    private DaysOfWeek(String label) {
> >        this.label = label;
> >    }
> >    public String getLabel() {
> >        return this.label;
> >    }
> >    public static DaysOfWeek getByLabel(String label) {
> >        for(DaysOfWeek day : dayList) {
> >            if(day.getLabel().equals(label)) {
> >                return day;
> >            }
> >        }
> >        return null;
> >    }
> >    public static final List<DaysOfWeek> getAllList() {
> >        return dayList;
> >    }
> > }
> > ------------------------------------------------------------
> >
> > JSF Converter:
> > ------------------------------------------------------------
> > package com.facets;
> > import javax.faces.component.UIComponent;
> > import javax.faces.context.FacesContext; import
> > javax.faces.convert.Converter; import
> > javax.faces.convert.ConverterException;
> > public class DayEnumConverter implements Converter {
> >    public Object getAsObject(FacesContext context, UIComponent
> > component, String value)
> >          throws ConverterException {
> >        DaysOfWeek day = DaysOfWeek.getByLabel(value);
> >        return (day==null ? DaysOfWeek.SUNDAY : day);
> >    }
> >    public String getAsString(FacesContext context, UIComponent
> > component, Object object)
> >          throws ConverterException {
> >        if(object instanceof String) {
> >            return (String)object;
> >        }
> >        if(!(object instanceof DaysOfWeek)) {
> >            return "";
> >        }
> >        return ((DaysOfWeek)object).getLabel();
> >    }
> > }
> > ------------------------------------------------------------
> >
> >
> > Spring definition to get a list of the enums:
> > ------------------------------------------------------------
> > <bean id="daysEnumList"
> >      class="com.facets.DaysOfWeek"
> >      factory-method="getAllList" />
> > ------------------------------------------------------------
> >
> >
> > A simple drop down list made from an ArrayList of Enums.
> > ------------------------------------------------------------
> > <h:outputLabel for="daysOfWeekList" value="#{labels.dayList}" />
> > <t:selectOneMenu id="daysOfWeekList"
> >                 value="#{calendar.day}">
> >    <t:selectItems var="day"
> >                   value="#{daysEnumList}"
> >                   itemLabel="#{day.label}"
> >                   itemValue="#{day.label}"/>
> >    <f:converter converterId="dayEnumConverter"/> </t:selectOneMenu>
> > ------------------------------------------------------------
> >
> > Assumptions:
> > 1) I'm using SpringBeanVariableResolver to get the "daysEnumList"
> > object from the spring application context.
> > 2) The problem goes away if I take the daysOfWeekList selectOneMenu
> > control off the jsp page.  Also, there is another dropdown list using
> > only strings and that works just fine.
> > 3) The converter is properly listed in the faces-context.xml file, as
> > it is called normally during the JSF lifecycle - at least until the bug
> hits.
> >
> >
> > The Bug:
> > When I submit a form containing the above selectOneMenu control, the
> > list of which is created from a Java 5 Enum, I get this error in the
> logs:
> >
> > ------------------------------------------------------------
> > DEBUG | 2010-01-06 13:28:53,912 | LifecycleImpl.java:178 | exiting
> > from lifecycle.execute in RESTORE_VIEW(1) because getRenderResponse is
> > true from one of the after listeners
> > ------------------------------------------------------------
> >
> > This means that backing bean never gets bound to the form values, and
> > the form action is never called.  I never get past the "Apply Request
> Values"
> > step of the faces lifecycle.
> >
> > Take a look at this stack trace...
> >
> > ------------------------------------------------------------
> > Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue:
> > 'weblogic.kernel.Default (self-tuning)'] (Suspended)
> > _SelectItemsUtil.matchValue(Object, Iterator) line: 65
> > HtmlSelectOneMenu(UISelectOne).validateValue(FacesContext, Object) line:
> > 77
> >  HtmlSelectOneMenu(UIInput).validate(FacesContext) line: 428
> >  HtmlSelectOneMenu(UIInput).processValidators(FacesContext) line: 245
> >  HtmlTag(UIComponentBase).processValidators(FacesContext) line: 866
> >  HtmlForm(UIForm).processValidators(FacesContext) line: 78
> >  UIViewRoot(UIComponentBase).processValidators(FacesContext) line: 866
> >  UIViewRoot.processValidators(FacesContext) line: 169
> >  ProcessValidationsExecutor.execute(FacesContext) line: 32
> > LifecycleImpl.executePhase(FacesContext, PhaseExecutor,
> > PhaseListenerManager) line: 105
> >  LifecycleImpl.execute(FacesContext) line: 80
> > FacesServlet.service(ServletRequest, ServletResponse) line: 143
> >  StubSecurityHelper$ServletServiceAction.run() line: 225
> > StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest,
> > ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet)
> > line: 127  ServletStubImpl.execute(ServletRequest, ServletResponse,
> > FilterChainImpl)
> > line: 283
> >  TailFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> > line: 26  FilterChainImpl.doFilter(ServletRequest, ServletResponse)
> > line: 42  ExtensionsFilter.doFilter(ServletRequest, ServletResponse,
> > FilterChain)
> > line: 246
> >  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
> > NavigationFilter.doFilter(ServletRequest, ServletResponse,
> > FilterChain)
> > line: 93
> >  DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest,
> > ServletResponse, FilterChain) line: 236
> > DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse,
> > FilterChain) line: 167
> >  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
> > ExtensionsFilter.doFilter(ServletRequest, ServletResponse,
> > FilterChain)
> > line: 301
> >  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
> >  WebAppServletContext$ServletInvocationAction.run() line: 3212
> > AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321
> > SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject,
> > PrivilegedAction) line: 121
> >  WebAppServletContext.securedExecute(HttpServletRequest,
> > HttpServletResponse, boolean) line: 1983
> > WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl)
> > line: 1890
> >  ServletRequestImpl.run() line: 1344
> >  ExecuteThread.execute(Runnable) line: 209
> >  ExecuteThread.run() line: 181
> > ------------------------------------------------------------
> >
> > The bug is at line 65 of _SelectItemsUtil.
> >
> > ------------------------------------------------------------
> > 44 public static boolean matchValue(Object value,
> > 45                 Iterator selectItemsIter)
> > 46 {
> > 47     while (selectItemsIter.hasNext())
> > 48     {
> > 49         SelectItem item = (SelectItem) selectItemsIter.next();
> > 50         if (item instanceof SelectItemGroup)
> > 51         {
> > 52             SelectItemGroup itemgroup = (SelectItemGroup) item;
> > 53            SelectItem[] selectItems = itemgroup.getSelectItems();
> > 54             if (selectItems != null
> > 55                             && selectItems.length > 0
> > 56                             && matchValue(value, Arrays.asList(
> > 57                                             selectItems).iterator()))
> > 58             {
> > 59                 return true;
> > 60             }
> > 61         }
> > 62         else
> > 63         {
> > 64             Object itemValue = item.getValue();
> > 65             if (value==itemValue || (itemValue.equals(value)))
> > 66             {
> > 67                 return true;
> > 68             }
> > 69         }
> > 70     }
> > 71     return false;
> > 72 }
> > ------------------------------------------------------------
> >
> >
> > The problem is that at this point, the "value" is the value that was
> > selected in the dropdown.  The CONVERTED value - compliments of line
> > 428 in the UIInput class:
> >
> > ------------------------------------------------------------
> > 424 Object convertedValue = getConvertedValue(context,
> > submittedValue);
> > 425
> > 426 if (!isValid()) return;
> > 427
> > 428 validateValue(context, convertedValue);
> > ------------------------------------------------------------
> >
> > BUT - the "item" object is from an Iterator created on line 77 of
> > UISelectOne, which only has the string values of the select component.
> >
> > This means when "item.getValue()" is called on line 64 (above), you
> > are getting the string value from whatever selectitem entry is
> > currently targeted - and of course the object version of the selected
> > value isn't going to equal its non-converted string value.
> >
> > Either I'm missing something and there's some configuration I missed
> > to fix this (which would make this behaviour a horrible default), or
> > this is a pretty big bug that just cost me five hours of development
> time.
> >
> > If no one cares about JSF 1.1 implementations anymore, just let me
> > know and I'll not spend the time posting here.  Otherwise, any chance
> > this could be fixed?
> >
> > ___________________________________________________________
> > John O'Grady
> > Dragon Tamer
> >
> > Human beings, who are almost unique in having the ability to learn
> > from the experience of others, are also remarkable for their apparent
> > disinclination to do so.
> > - Douglas Adams
> >
> > Those who do not learn from history are doomed to repeat it.
> > - George Santayana
> >
> > Qui tacet consentit
> > (Silence implies consent)
> > ___________________________________________________________
> >
> >
>

RE: Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the missing conversion

Posted by "Ogrady, John " <jo...@citi.com>.
 I do so love "secret sauce" answers.  I'm not surprised that it was something I did (or didn't do).  This is my first foray into JSF teritory after years of Spring MVC, Struts, and Webwork, and faces is by far the most frustrating framework of all.

Thanks for the quick reply!

___________________________________________________________
John O'Grady, Dragon Tamer
The important thing is not to stop questioning. - Albert Einstein
___________________________________________________________


-----Original Message-----
From: sethfromaustria@gmail.com [mailto:sethfromaustria@gmail.com] On Behalf Of Jakob Korherr
Sent: Wednesday, January 06, 2010 3:09 PM
To: MyFaces Discussion
Subject: Re: Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the missing conversion

Hi John,

Use >itemValue="#{day}"< and it will work.

Regards,
Jakob Korherr


2010/1/6 Ogrady, John <jo...@citi.com>

> Java 1.5
> MyFaces 1.1.7
> Tomahawk 1.1.9
> Spring 2.5.6
> Weblogic 9.2.3
>
>
> All I wanted to do was display a drop-down list of enums.  It seemed
> simple enough, with examples all over the 'net.  Apparently, myfaces
> has decided that using anything other than strings for a drop down is "no - not yours".
>
> The setup:
>
>
> My example enum:
> ------------------------------------------------------------
> package com.facets;
> import java.util.Arrays;
> import java.util.List;
> public enum DaysOfWeek {
>    SUNDAY("Sunday"),
>    MONDAY("Monday"),
>    TUESDAY("Tuesday"),
>    WEDNESDAY("Wednesday"),
>    THURSDAY("Thursday"),
>    FRIDAY("Friday"),
>    SATURDAY("Saturday");
>
>    private static List<DaysOfWeek> dayList =
> Arrays.asList(DaysOfWeek.values());
>
>    private String label;
>
>    private DaysOfWeek(String label) {
>        this.label = label;
>    }
>    public String getLabel() {
>        return this.label;
>    }
>    public static DaysOfWeek getByLabel(String label) {
>        for(DaysOfWeek day : dayList) {
>            if(day.getLabel().equals(label)) {
>                return day;
>            }
>        }
>        return null;
>    }
>    public static final List<DaysOfWeek> getAllList() {
>        return dayList;
>    }
> }
> ------------------------------------------------------------
>
> JSF Converter:
> ------------------------------------------------------------
> package com.facets;
> import javax.faces.component.UIComponent;
> import javax.faces.context.FacesContext; import
> javax.faces.convert.Converter; import
> javax.faces.convert.ConverterException;
> public class DayEnumConverter implements Converter {
>    public Object getAsObject(FacesContext context, UIComponent
> component, String value)
>          throws ConverterException {
>        DaysOfWeek day = DaysOfWeek.getByLabel(value);
>        return (day==null ? DaysOfWeek.SUNDAY : day);
>    }
>    public String getAsString(FacesContext context, UIComponent
> component, Object object)
>          throws ConverterException {
>        if(object instanceof String) {
>            return (String)object;
>        }
>        if(!(object instanceof DaysOfWeek)) {
>            return "";
>        }
>        return ((DaysOfWeek)object).getLabel();
>    }
> }
> ------------------------------------------------------------
>
>
> Spring definition to get a list of the enums:
> ------------------------------------------------------------
> <bean id="daysEnumList"
>      class="com.facets.DaysOfWeek"
>      factory-method="getAllList" />
> ------------------------------------------------------------
>
>
> A simple drop down list made from an ArrayList of Enums.
> ------------------------------------------------------------
> <h:outputLabel for="daysOfWeekList" value="#{labels.dayList}" />
> <t:selectOneMenu id="daysOfWeekList"
>                 value="#{calendar.day}">
>    <t:selectItems var="day"
>                   value="#{daysEnumList}"
>                   itemLabel="#{day.label}"
>                   itemValue="#{day.label}"/>
>    <f:converter converterId="dayEnumConverter"/> </t:selectOneMenu>
> ------------------------------------------------------------
>
> Assumptions:
> 1) I'm using SpringBeanVariableResolver to get the "daysEnumList"
> object from the spring application context.
> 2) The problem goes away if I take the daysOfWeekList selectOneMenu
> control off the jsp page.  Also, there is another dropdown list using
> only strings and that works just fine.
> 3) The converter is properly listed in the faces-context.xml file, as
> it is called normally during the JSF lifecycle - at least until the bug hits.
>
>
> The Bug:
> When I submit a form containing the above selectOneMenu control, the
> list of which is created from a Java 5 Enum, I get this error in the logs:
>
> ------------------------------------------------------------
> DEBUG | 2010-01-06 13:28:53,912 | LifecycleImpl.java:178 | exiting
> from lifecycle.execute in RESTORE_VIEW(1) because getRenderResponse is
> true from one of the after listeners
> ------------------------------------------------------------
>
> This means that backing bean never gets bound to the form values, and
> the form action is never called.  I never get past the "Apply Request Values"
> step of the faces lifecycle.
>
> Take a look at this stack trace...
>
> ------------------------------------------------------------
> Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue:
> 'weblogic.kernel.Default (self-tuning)'] (Suspended)
> _SelectItemsUtil.matchValue(Object, Iterator) line: 65
> HtmlSelectOneMenu(UISelectOne).validateValue(FacesContext, Object) line:
> 77
>  HtmlSelectOneMenu(UIInput).validate(FacesContext) line: 428
>  HtmlSelectOneMenu(UIInput).processValidators(FacesContext) line: 245
>  HtmlTag(UIComponentBase).processValidators(FacesContext) line: 866
>  HtmlForm(UIForm).processValidators(FacesContext) line: 78
>  UIViewRoot(UIComponentBase).processValidators(FacesContext) line: 866
>  UIViewRoot.processValidators(FacesContext) line: 169
>  ProcessValidationsExecutor.execute(FacesContext) line: 32
> LifecycleImpl.executePhase(FacesContext, PhaseExecutor,
> PhaseListenerManager) line: 105
>  LifecycleImpl.execute(FacesContext) line: 80
> FacesServlet.service(ServletRequest, ServletResponse) line: 143
>  StubSecurityHelper$ServletServiceAction.run() line: 225
> StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest,
> ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet)
> line: 127  ServletStubImpl.execute(ServletRequest, ServletResponse,
> FilterChainImpl)
> line: 283
>  TailFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> line: 26  FilterChainImpl.doFilter(ServletRequest, ServletResponse)
> line: 42  ExtensionsFilter.doFilter(ServletRequest, ServletResponse,
> FilterChain)
> line: 246
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
> NavigationFilter.doFilter(ServletRequest, ServletResponse,
> FilterChain)
> line: 93
>  DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest,
> ServletResponse, FilterChain) line: 236
> DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse,
> FilterChain) line: 167
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
> ExtensionsFilter.doFilter(ServletRequest, ServletResponse,
> FilterChain)
> line: 301
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
>  WebAppServletContext$ServletInvocationAction.run() line: 3212
> AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321
> SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject,
> PrivilegedAction) line: 121
>  WebAppServletContext.securedExecute(HttpServletRequest,
> HttpServletResponse, boolean) line: 1983
> WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl)
> line: 1890
>  ServletRequestImpl.run() line: 1344
>  ExecuteThread.execute(Runnable) line: 209
>  ExecuteThread.run() line: 181
> ------------------------------------------------------------
>
> The bug is at line 65 of _SelectItemsUtil.
>
> ------------------------------------------------------------
> 44 public static boolean matchValue(Object value,
> 45                 Iterator selectItemsIter)
> 46 {
> 47     while (selectItemsIter.hasNext())
> 48     {
> 49         SelectItem item = (SelectItem) selectItemsIter.next();
> 50         if (item instanceof SelectItemGroup)
> 51         {
> 52             SelectItemGroup itemgroup = (SelectItemGroup) item;
> 53            SelectItem[] selectItems = itemgroup.getSelectItems();
> 54             if (selectItems != null
> 55                             && selectItems.length > 0
> 56                             && matchValue(value, Arrays.asList(
> 57                                             selectItems).iterator()))
> 58             {
> 59                 return true;
> 60             }
> 61         }
> 62         else
> 63         {
> 64             Object itemValue = item.getValue();
> 65             if (value==itemValue || (itemValue.equals(value)))
> 66             {
> 67                 return true;
> 68             }
> 69         }
> 70     }
> 71     return false;
> 72 }
> ------------------------------------------------------------
>
>
> The problem is that at this point, the "value" is the value that was
> selected in the dropdown.  The CONVERTED value - compliments of line
> 428 in the UIInput class:
>
> ------------------------------------------------------------
> 424 Object convertedValue = getConvertedValue(context,
> submittedValue);
> 425
> 426 if (!isValid()) return;
> 427
> 428 validateValue(context, convertedValue);
> ------------------------------------------------------------
>
> BUT - the "item" object is from an Iterator created on line 77 of
> UISelectOne, which only has the string values of the select component.
>
> This means when "item.getValue()" is called on line 64 (above), you
> are getting the string value from whatever selectitem entry is
> currently targeted - and of course the object version of the selected
> value isn't going to equal its non-converted string value.
>
> Either I'm missing something and there's some configuration I missed
> to fix this (which would make this behaviour a horrible default), or
> this is a pretty big bug that just cost me five hours of development time.
>
> If no one cares about JSF 1.1 implementations anymore, just let me
> know and I'll not spend the time posting here.  Otherwise, any chance
> this could be fixed?
>
> ___________________________________________________________
> John O'Grady
> Dragon Tamer
>
> Human beings, who are almost unique in having the ability to learn
> from the experience of others, are also remarkable for their apparent
> disinclination to do so.
> - Douglas Adams
>
> Those who do not learn from history are doomed to repeat it.
> - George Santayana
>
> Qui tacet consentit
> (Silence implies consent)
> ___________________________________________________________
>
>

Re: Bad Bug: SelectOneMenu, SelectItems, _SystemItemsUtil, and the missing conversion

Posted by Jakob Korherr <ja...@gmail.com>.
Hi John,

Use »itemValue="#{day}"« and it will work.

Regards,
Jakob Korherr


2010/1/6 Ogrady, John <jo...@citi.com>

> Java 1.5
> MyFaces 1.1.7
> Tomahawk 1.1.9
> Spring 2.5.6
> Weblogic 9.2.3
>
>
> All I wanted to do was display a drop-down list of enums.  It seemed simple
> enough, with examples all over the 'net.  Apparently, myfaces has decided
> that using anything other than strings for a drop down is "no - not yours".
>
> The setup:
>
>
> My example enum:
> ------------------------------------------------------------
> package com.facets;
> import java.util.Arrays;
> import java.util.List;
> public enum DaysOfWeek {
>    SUNDAY("Sunday"),
>    MONDAY("Monday"),
>    TUESDAY("Tuesday"),
>    WEDNESDAY("Wednesday"),
>    THURSDAY("Thursday"),
>    FRIDAY("Friday"),
>    SATURDAY("Saturday");
>
>    private static List<DaysOfWeek> dayList =
> Arrays.asList(DaysOfWeek.values());
>
>    private String label;
>
>    private DaysOfWeek(String label) {
>        this.label = label;
>    }
>    public String getLabel() {
>        return this.label;
>    }
>    public static DaysOfWeek getByLabel(String label) {
>        for(DaysOfWeek day : dayList) {
>            if(day.getLabel().equals(label)) {
>                return day;
>            }
>        }
>        return null;
>    }
>    public static final List<DaysOfWeek> getAllList() {
>        return dayList;
>    }
> }
> ------------------------------------------------------------
>
> JSF Converter:
> ------------------------------------------------------------
> package com.facets;
> import javax.faces.component.UIComponent;
> import javax.faces.context.FacesContext;
> import javax.faces.convert.Converter;
> import javax.faces.convert.ConverterException;
> public class DayEnumConverter implements Converter {
>    public Object getAsObject(FacesContext context, UIComponent component,
> String value)
>          throws ConverterException {
>        DaysOfWeek day = DaysOfWeek.getByLabel(value);
>        return (day==null ? DaysOfWeek.SUNDAY : day);
>    }
>    public String getAsString(FacesContext context, UIComponent component,
> Object object)
>          throws ConverterException {
>        if(object instanceof String) {
>            return (String)object;
>        }
>        if(!(object instanceof DaysOfWeek)) {
>            return "";
>        }
>        return ((DaysOfWeek)object).getLabel();
>    }
> }
> ------------------------------------------------------------
>
>
> Spring definition to get a list of the enums:
> ------------------------------------------------------------
> <bean id="daysEnumList"
>      class="com.facets.DaysOfWeek"
>      factory-method="getAllList" />
> ------------------------------------------------------------
>
>
> A simple drop down list made from an ArrayList of Enums.
> ------------------------------------------------------------
> <h:outputLabel for="daysOfWeekList" value="#{labels.dayList}" />
> <t:selectOneMenu id="daysOfWeekList"
>                 value="#{calendar.day}">
>    <t:selectItems var="day"
>                   value="#{daysEnumList}"
>                   itemLabel="#{day.label}"
>                   itemValue="#{day.label}"/>
>    <f:converter converterId="dayEnumConverter"/>
> </t:selectOneMenu>
> ------------------------------------------------------------
>
> Assumptions:
> 1) I'm using SpringBeanVariableResolver to get the "daysEnumList" object
> from the spring application context.
> 2) The problem goes away if I take the daysOfWeekList selectOneMenu control
> off the jsp page.  Also, there is another dropdown list using only strings
> and that works just fine.
> 3) The converter is properly listed in the faces-context.xml file, as it is
> called normally during the JSF lifecycle - at least until the bug hits.
>
>
> The Bug:
> When I submit a form containing the above selectOneMenu control, the list
> of which is created from a Java 5 Enum, I get this error in the logs:
>
> ------------------------------------------------------------
> DEBUG | 2010-01-06 13:28:53,912 | LifecycleImpl.java:178 | exiting from
> lifecycle.execute in RESTORE_VIEW(1) because getRenderResponse is true from
> one of the after listeners
> ------------------------------------------------------------
>
> This means that backing bean never gets bound to the form values, and the
> form action is never called.  I never get past the "Apply Request Values"
> step of the faces lifecycle.
>
> Take a look at this stack trace...
>
> ------------------------------------------------------------
> Daemon Thread [[ACTIVE] ExecuteThread: '0' for queue:
> 'weblogic.kernel.Default (self-tuning)'] (Suspended)
>  _SelectItemsUtil.matchValue(Object, Iterator) line: 65
>  HtmlSelectOneMenu(UISelectOne).validateValue(FacesContext, Object) line:
> 77
>  HtmlSelectOneMenu(UIInput).validate(FacesContext) line: 428
>  HtmlSelectOneMenu(UIInput).processValidators(FacesContext) line: 245
>  HtmlTag(UIComponentBase).processValidators(FacesContext) line: 866
>  HtmlForm(UIForm).processValidators(FacesContext) line: 78
>  UIViewRoot(UIComponentBase).processValidators(FacesContext) line: 866
>  UIViewRoot.processValidators(FacesContext) line: 169
>  ProcessValidationsExecutor.execute(FacesContext) line: 32
>  LifecycleImpl.executePhase(FacesContext, PhaseExecutor,
> PhaseListenerManager) line: 105
>  LifecycleImpl.execute(FacesContext) line: 80
>  FacesServlet.service(ServletRequest, ServletResponse) line: 143
>  StubSecurityHelper$ServletServiceAction.run() line: 225
>  StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest,
> ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet) line: 127
>  ServletStubImpl.execute(ServletRequest, ServletResponse, FilterChainImpl)
> line: 283
>  TailFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 26
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
>  ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> line: 246
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
>  NavigationFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> line: 93
>  DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest,
> ServletResponse, FilterChain) line: 236
>  DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse,
> FilterChain) line: 167
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
>  ExtensionsFilter.doFilter(ServletRequest, ServletResponse, FilterChain)
> line: 301
>  FilterChainImpl.doFilter(ServletRequest, ServletResponse) line: 42
>  WebAppServletContext$ServletInvocationAction.run() line: 3212
>  AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321
>  SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject,
> PrivilegedAction) line: 121
>  WebAppServletContext.securedExecute(HttpServletRequest,
> HttpServletResponse, boolean) line: 1983
>  WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl)
> line: 1890
>  ServletRequestImpl.run() line: 1344
>  ExecuteThread.execute(Runnable) line: 209
>  ExecuteThread.run() line: 181
> ------------------------------------------------------------
>
> The bug is at line 65 of _SelectItemsUtil.
>
> ------------------------------------------------------------
> 44 public static boolean matchValue(Object value,
> 45                 Iterator selectItemsIter)
> 46 {
> 47     while (selectItemsIter.hasNext())
> 48     {
> 49         SelectItem item = (SelectItem) selectItemsIter.next();
> 50         if (item instanceof SelectItemGroup)
> 51         {
> 52             SelectItemGroup itemgroup = (SelectItemGroup) item;
> 53            SelectItem[] selectItems = itemgroup.getSelectItems();
> 54             if (selectItems != null
> 55                             && selectItems.length > 0
> 56                             && matchValue(value, Arrays.asList(
> 57                                             selectItems).iterator()))
> 58             {
> 59                 return true;
> 60             }
> 61         }
> 62         else
> 63         {
> 64             Object itemValue = item.getValue();
> 65             if (value==itemValue || (itemValue.equals(value)))
> 66             {
> 67                 return true;
> 68             }
> 69         }
> 70     }
> 71     return false;
> 72 }
> ------------------------------------------------------------
>
>
> The problem is that at this point, the "value" is the value that was
> selected in the dropdown.  The CONVERTED value - compliments of line 428 in
> the UIInput class:
>
> ------------------------------------------------------------
> 424 Object convertedValue = getConvertedValue(context, submittedValue);
> 425
> 426 if (!isValid()) return;
> 427
> 428 validateValue(context, convertedValue);
> ------------------------------------------------------------
>
> BUT - the "item" object is from an Iterator created on line 77 of
> UISelectOne, which only has the string values of the select component.
>
> This means when "item.getValue()" is called on line 64 (above), you are
> getting the string value from whatever selectitem entry is currently
> targeted - and of course the object version of the selected value isn't
> going to equal its non-converted string value.
>
> Either I'm missing something and there's some configuration I missed to fix
> this (which would make this behaviour a horrible default), or this is a
> pretty big bug that just cost me five hours of development time.
>
> If no one cares about JSF 1.1 implementations anymore, just let me know and
> I'll not spend the time posting here.  Otherwise, any chance this could be
> fixed?
>
> ___________________________________________________________
> John O'Grady
> Dragon Tamer
>
> Human beings, who are almost unique in having the ability to learn
> from the experience of others, are also remarkable for their apparent
> disinclination to do so.
> - Douglas Adams
>
> Those who do not learn from history are doomed to repeat it.
> - George Santayana
>
> Qui tacet consentit
> (Silence implies consent)
> ___________________________________________________________
>
>