4

Here's my page:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:cpanel="http://java.sun.com/jsf/composite/components/cpanel"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.prime.com.tr/ui"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/cpanelLayout.xhtml">
            <ui:define name="metadata">
                <f:metadata>
                    <f:viewParam name="id" value="#{linkDoctorBean.incomingDoctorId}" required="true"/>
                    <f:event type="preRenderView" listener="#{linkDoctorBean.preRenderView}"/>
                </f:metadata>
            </ui:define>
            <ui:define name="cpanelContent">
                <cpanel:adddedClinicDoctor doctor="#{linkDoctorBean.incomingDoctor}" clinic="#{linkDoctorBean.clinic}"/>
                <h:form id="form">
                    <div>
                        <h:panelGrid columns="2">
                            <p:selectOneMenu id="doctor"
                                             value="#{linkDoctorBean.doctor}"
                                             converter="#{doctorConverter}"
                                             effect="fade"
                                             var="d"
                                             height="180"
                                             validator="#{linkDoctorBean.validateDoctor}">
                                <f:selectItem itemLabel="Select a doctor" itemValue="" noSelectionOption="true"/>
                                <f:selectItems value="#{linkDoctorBean.doctors}" var="doctor" itemLabel="#{stringBean.doctorToString(doctor)}" itemValue="#{doctor}"/>
                                <p:column>
                                    <p:graphicImage value="#{applicationBean.baseResourceUrl}/doc-photo/small/#{d.urlDisplayName}"/>
                                </p:column>
                                <p:column>
                                    #{stringBean.doctorToString(d)}
                                </p:column>
                            </p:selectOneMenu>
                            <p:message id="doctorMessage" for="doctor"/>
                        </h:panelGrid>
                    </div>
                    <div style="margin-top:20px;">
                        <p:commandButton value="Link" action="#{linkDoctorBean.submit()}" update="doctorMessage" process="@all"/>
                        <h:link value="Cancel" outcome="/pages/cpanel/manage-doctors" style="margin-left:10px;"/>
                    </div>
                </h:form>
            </ui:define>
        </ui:composition>
    </h:body>
</html>

When I add ajax="false" to the <p:commandButton/>, it works just fine - the linkDoctorBean.submit() method is called.

With ajax enabled, however, the action method will be called if the form never fails validation in the current view, but if the form fails validation once and is subsequently submitted, the error message is cleared, but the submit() method is never called.

Edit: I also tried replacing the p:commandButton with this:

<h:commandButton value="Link" action="#{linkDoctorBean.submit()}">
    <f:ajax execute="@all" render="doctorMessage"/>
</h:commandButton>

The behaviour is exactly the same. In both cases, the validation message appears to be cleared when a valid option is submitted. So the update/render is happening, just not the process/execute. All subsequent attempts fail to execute the action, so the form is effectively dead until the next time the page is loaded.

Edit: This doesn't seem to be a PrimeFaces issue. Here's the form without PrimeFaces:

    <h:form id="form">
        <div>
            <h:panelGrid columns="2">
                <h:selectOneMenu id="doctor"
                                 value="#{linkDoctorBean.doctor}"
                                 converter="#{doctorConverter}"
                                 style="width:200px;"
                                 required="true"
                                 requiredMessage="Please select a doctor"
                                 validator="#{linkDoctorBean.validateDoctor}">
                    <f:selectItem itemLabel="Select a doctor" itemValue="#{null}" noSelectionOption="true"/>
                    <f:selectItems value="#{linkDoctorBean.doctors}" var="doctor" itemLabel="#{stringBean.doctorToString(doctor)}" itemValue="#{doctor}"/>
                </h:selectOneMenu>
                <h:message id="doctorMessage" for="doctor"/>
            </h:panelGrid>
        </div>
        <div style="margin-top:20px;">
            <h:commandButton value="Link" action="#{linkDoctorBean.submit()}">
                <f:ajax execute="@form" render="doctorMessage"/>
            </h:commandButton>
            <h:link value="Cancel" outcome="/pages/cpanel/manage-doctors" style="margin-left:10px;"/>
        </div>
    </h:form>

There are some minor differences now, such as required and the place-holder select item's value being null. Perhaps it would be best to focus on what might be going wrong with this latest snippet and forget PrimeFaces for a moment.

In case it helps, here's the DoctorConverter class:

@Named("doctorConverter")
@RequestScoped
public class DoctorConverter implements Converter, Serializable
{
    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value)
    {
        UIInput in = (UIInput) component;
        String s = in.getSubmittedValue() != null ? in.getSubmittedValue().toString() : "";

        try
        {
            long doctorId = Long.parseLong(s);
            Doctor doctor = doctorDao.findByDoctorId(doctorId);
            if (doctor == null)
            {
                throw new Exception("Doctor not found.");
            }
            return doctor;
        }
        catch (Exception e)
        {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Please enter a valid doctor", null), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value)
    {
        if (value == null || value.toString().length() == 0)
        {
            return "";
        }
        Doctor doctor = (Doctor) value;
        return doctor.getDoctorId().toString();
    }

    @EJB
    private DoctorDao doctorDao;
}

Edit: ... and here are a couple of methods from the Doctor entity class:

@Override
public int hashCode()
{
    int hash = 0;
    hash += (doctorId != null ? doctorId.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object)
{
    if (!(object instanceof Doctor))
    {
        return false;
    }
    Doctor other = (Doctor) object;
    if ((this.doctorId == null && other.doctorId != null) || (this.doctorId != null && !this.doctorId.equals(other.doctorId)))
    {
        return false;
    }
    return true;
}

Edit: As discussed in comments, DoctorConverter.getAsObject is called three times. Here are the stack traces for the three calls in order:

1

at java.lang.Thread.dumpStack(Thread.java:1342)
at com.localgp.jsf.converter.DoctorConverter.getAsObject(DoctorConverter.java:34)
at com.localgp.jsf.converter.org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.getAsObject(org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.java)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getConvertedValue(HtmlBasicInputRenderer.java:171)
at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectOneValue(MenuRenderer.java:202)
at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:319)
at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
at javax.faces.component.UIInput.validate(UIInput.java:960)
at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
at javax.faces.component.UIInput.processValidators(UIInput.java:698)
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
at javax.faces.component.UIForm.processValidators(UIForm.java:253)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:508)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1589)
at javax.faces.component.UIForm.visitTree(UIForm.java:344)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:376)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:252)
at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1170)
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at com.localgp.NoCacheFilter.doFilter(NoCacheFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.ApplicationDispatcher.doInvoke(ApplicationDispatcher.java:785)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:649)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:483)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:454)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:350)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:300)
at org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:213)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:171)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:381)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
at com.sun.grizzly.comet.CometEngine.executeServlet(CometEngine.java:444)
at com.sun.grizzly.comet.CometEngine.handle(CometEngine.java:308)
at com.sun.grizzly.comet.CometAsyncFilter.doFilter(CometAsyncFilter.java:87)
at com.sun.grizzly.arp.DefaultAsyncExecutor.invokeFilters(DefaultAsyncExecutor.java:171)
at com.sun.grizzly.arp.DefaultAsyncExecutor.interrupt(DefaultAsyncExecutor.java:143)
at com.sun.grizzly.arp.AsyncProcessorTask.doTask(AsyncProcessorTask.java:94)
at com.sun.grizzly.http.TaskBase.run(TaskBase.java:193)
at com.sun.grizzly.http.TaskBase.execute(TaskBase.java:175)
at com.sun.grizzly.arp.DefaultAsyncHandler.handle(DefaultAsyncHandler.java:145)
at com.sun.grizzly.arp.AsyncProtocolFilter.execute(AsyncProtocolFilter.java:204)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)

2

at java.lang.Thread.dumpStack(Thread.java:1342)
at com.localgp.jsf.converter.DoctorConverter.getAsObject(DoctorConverter.java:34)
at com.localgp.jsf.converter.org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.getAsObject(org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.java)
at javax.faces.component.SelectUtils.doConversion(SelectUtils.java:191)
at javax.faces.component.SelectUtils.matchValue(SelectUtils.java:99)
at javax.faces.component.UISelectOne.validateValue(UISelectOne.java:153)
at javax.faces.component.UIInput.validate(UIInput.java:967)
at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
at javax.faces.component.UIInput.processValidators(UIInput.java:698)
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
at javax.faces.component.UIForm.processValidators(UIForm.java:253)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:508)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1589)
at javax.faces.component.UIForm.visitTree(UIForm.java:344)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:376)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:252)
at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1170)
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at com.localgp.NoCacheFilter.doFilter(NoCacheFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.ApplicationDispatcher.doInvoke(ApplicationDispatcher.java:785)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:649)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:483)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:454)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:350)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:300)
at org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:213)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:171)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:381)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
at com.sun.grizzly.comet.CometEngine.executeServlet(CometEngine.java:444)
at com.sun.grizzly.comet.CometEngine.handle(CometEngine.java:308)
at com.sun.grizzly.comet.CometAsyncFilter.doFilter(CometAsyncFilter.java:87)
at com.sun.grizzly.arp.DefaultAsyncExecutor.invokeFilters(DefaultAsyncExecutor.java:171)
at com.sun.grizzly.arp.DefaultAsyncExecutor.interrupt(DefaultAsyncExecutor.java:143)
at com.sun.grizzly.arp.AsyncProcessorTask.doTask(AsyncProcessorTask.java:94)
at com.sun.grizzly.http.TaskBase.run(TaskBase.java:193)
at com.sun.grizzly.http.TaskBase.execute(TaskBase.java:175)
at com.sun.grizzly.arp.DefaultAsyncHandler.handle(DefaultAsyncHandler.java:145)
at com.sun.grizzly.arp.AsyncProtocolFilter.execute(AsyncProtocolFilter.java:204)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)

3

at java.lang.Thread.dumpStack(Thread.java:1342)
at com.localgp.jsf.converter.DoctorConverter.getAsObject(DoctorConverter.java:34)
at com.localgp.jsf.converter.org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.getAsObject(org$jboss$weld$bean-localgp-server-web-1$0-SNAPSHOT_war-ManagedBean-class_com$localgp$jsf$converter$DoctorConverter_$$_WeldClientProxy.java)
at javax.faces.component.SelectUtils.doConversion(SelectUtils.java:191)
at javax.faces.component.SelectUtils.valueIsNoSelectionOption(SelectUtils.java:150)
at javax.faces.component.UISelectOne.validateValue(UISelectOne.java:159)
at javax.faces.component.UIInput.validate(UIInput.java:967)
at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
at javax.faces.component.UIInput.processValidators(UIInput.java:698)
at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1214)
at javax.faces.component.UIForm.processValidators(UIForm.java:253)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:508)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1589)
at javax.faces.component.UIForm.visitTree(UIForm.java:344)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1600)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:376)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:252)
at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1170)
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at com.localgp.NoCacheFilter.doFilter(NoCacheFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.ApplicationDispatcher.doInvoke(ApplicationDispatcher.java:785)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:649)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:483)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:454)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:350)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:300)
at org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:213)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:171)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:381)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:98)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:91)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:162)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:330)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:174)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:828)
at com.sun.grizzly.comet.CometEngine.executeServlet(CometEngine.java:444)
at com.sun.grizzly.comet.CometEngine.handle(CometEngine.java:308)
at com.sun.grizzly.comet.CometAsyncFilter.doFilter(CometAsyncFilter.java:87)
at com.sun.grizzly.arp.DefaultAsyncExecutor.invokeFilters(DefaultAsyncExecutor.java:171)
at com.sun.grizzly.arp.DefaultAsyncExecutor.interrupt(DefaultAsyncExecutor.java:143)
at com.sun.grizzly.arp.AsyncProcessorTask.doTask(AsyncProcessorTask.java:94)
at com.sun.grizzly.http.TaskBase.run(TaskBase.java:193)
at com.sun.grizzly.http.TaskBase.execute(TaskBase.java:175)
at com.sun.grizzly.arp.DefaultAsyncHandler.handle(DefaultAsyncHandler.java:145)
at com.sun.grizzly.arp.AsyncProtocolFilter.execute(AsyncProtocolFilter.java:204)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)

And here's the current form in the JSF page:

<h:form id="form">
    <!-- Validating here instead of at the selectOneMenu level due to its invalidity not being cleared. -->
    <div style="clear:both;">
        <h:panelGrid columns="2">
            <h:selectOneMenu id="doctor"
                             value="#{linkDoctorBean.doctor}"
                             converter="#{doctorConverter}"
                             converterMessage="Please select a doctor"
                             style="width:200px;"
                             required="true">
                <f:selectItem itemLabel="Select a doctor" itemValue="" noSelectionOption="true"/>
                <f:selectItems value="#{linkDoctorBean.doctors}" var="doctor" itemLabel="#{stringBean.doctorToString(doctor)}" itemValue="#{doctor}"/>
            </h:selectOneMenu>
            <p:message id="doctorMessage" for="doctor"/>
        </h:panelGrid>
    </div>
    <div style="margin-top:20px;">
        <h:commandButton value="Link" action="#{linkDoctorBean.submit()}">
            <f:ajax execute="@form" render="doctorMessage"/>
        </h:commandButton>
        <h:link value="Cancel" outcome="/pages/cpanel/manage-doctors" style="margin-left:10px;"/>
    </div>
</h:form>

Edit: Removed <f:ajax/> and DoctorConverter.getAsObject is still being called three times.

Steve
  • 8,066
  • 11
  • 70
  • 112
  • What JSF impl/version and PF version? Try `@form` instead of `@all`. – BalusC Oct 01 '11 at 12:07
  • Thanks. Tried `@form`, no difference. Mojarra 2.1.3 (GlassFish 3.1.1), PrimeFaces 3.0 M3. – Steve Oct 01 '11 at 12:31
  • @BalusC, please see the second edit. PrimeFaces is out of the picture now. – Steve Oct 01 '11 at 13:13
  • I can't reproduce your issue. Perhaps `validateDoctor()` is doing something wrong. By the way, in the converter you're supposed to get the submitted value from the 3rd argument `String value`. – BalusC Oct 01 '11 at 15:40
  • @BalusC, I thought you might be onto something with the converter, so I tried using `value` instead and added some trace writes. Interestingly, the converter is being called three times when I'd expect it to be called only once. On the second and third calls, `value` is empty, whereas `component.submittedValue` has the same value for all calls. Mind you, that was with PF, but I'm assuming it would behave the same with `h:selectOneMenu`. I will try, though. Also, I removed the validator because I realised that PF (or JSF) will ensure the value is from the list, which is what the validator does. – Steve Oct 01 '11 at 16:09
  • Without the validator, I'm getting this message for submissions that should be valid, even on the first submission attempt: `form:doctor: Validation Error: Value is not valid` – Steve Oct 01 '11 at 16:11
  • It should also be called only once. The one in my playground is by the way just a `@FacesConverter`. I will check with `@Named` and stuffs. Edit: as to the validation error, this indicates that `Doctor#equals()` has not returned `true` for any of the available items on the submitted and converted value. Is the `equals()` properly implemented? Edit: `@Named` converter also still calls only once, as expected. Don't you have something more into the view? – BalusC Oct 01 '11 at 16:13
  • Pretty sure I didn't fluff up `Doctor.equals()`. I just appended `Doctor.hashCode()` and `Doctor.equals()` to the question. Sorry if I'm slowing your march to 200,000 btw. – Steve Oct 01 '11 at 16:22
  • Haha. No problem :) Well, your `equals()` is not reflexive (i.e. `obj == this` check is missing, this should also pass for `null` IDs), but that shouldn't matter in this particular case. See also http://stackoverflow.com/questions/3181339/right-way-to-implement-equals-contract/3181374#3181374 I think your converter is the main culprit, but I don't see possible causes right now. That it is been called three times (you're talking about `getAsObject()` upon form submit, right?) is at least not a good sign. – BalusC Oct 01 '11 at 16:26
  • Yes, `getAsObject()` is being called three times. I haven't yet tried reverting to the html select to see if that would trigger 3 calls because the `required` attribute doesn't seem to work. Edit: Tried html select, still calling `getAsObject()` 3 times. – Steve Oct 01 '11 at 16:32
  • Note: I used your form example without PF components. It is also the sole form in the view. I didn't modify anything else (expect that I used a stub entity instead of "Doctor", my converter is also written a bit differently). Could you try to put a breakpoint on `getAsObject()` and see in the stack trace from where it is been called? Or just add `Thread.dumpStack();` to `getAsObject()` method. – BalusC Oct 01 '11 at 16:34
  • Stack traces added to question. Edit: Removed template from JSF page to make sure the form on that wasn't interfering. Behaviour remains the same. – Steve Oct 01 '11 at 16:48
  • Thank you, this is very helpful. At first sight, those traces suggests that the select item value of `` is **not** `instanceof Doctor`. Are you sure that `#{linkDoctorBean.doctors}` is a `List`? – BalusC Oct 01 '11 at 16:57
  • It sure is a `List`. Being painfully aware that generics don't exist at runtime, I just checked the DAO method and JPQL query and I'm definitely getting a list of doctors. Anyway `DoctorConverter.getAsString()` would surely fail with `ClassCastException` if that wasn't the case. I just removed the single `f:selectItem` and now I only have one call to `DoctorConverter.getAsObject`. But I really do need that placeholder `f:selectItem`. What to do? :( – Steve Oct 01 '11 at 17:05
  • Ah right, it's the other way round, the `noSelectionOption` is **not** `instanceof Doctor`. Your converter is indeed wrongly implemented. Use the `value` instead of `getSubmittedValue()`. – BalusC Oct 01 '11 at 17:15

2 Answers2

6

Your converter is broken. It has to convert the value based on the value argument, not the UIInput#getSubmittedValue(). The noSelectionOption value is namely also passed through the converter (as the 3rd trace hints). If your real submitted value equals the noSelectionOption value, then the value is considered as not valid, hence the validation error.

I also suggest other minor rewrites; empty strings should be nulls.

@Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
    if (modelValue == null) {
        return "";
    }

    if (modelValue instanceof Doctor) {
        Long id = ((Doctor) modelValue).getId();
        return (id != null) ? String.valueOf(id) : null;
    } else {
        throw new ConverterException(new FacesMessage(modelValue + " is not a valid Doctor"));
    }
}

@Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
    if (submittedValue == null || submittedValue.isEmpty()) {
        return null;
    }

    try {
        Long id = Long.valueOf(submittedValue);
        return doctorService.find(id);
    } catch (NumberFormatException e) {
        throw new ConverterException(new FacesMessage(submittedValue + " is not a valid Doctor ID"));
    }
}

Note that I have this in my web.xml as well:

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

Update: as per the comments, thanks to <h:messages/> there turns out to be something else which is been required. Finally it turns out to be a <f:viewParam required="true"> which is been reinvoked on ajax postback. To prevent this, instead use

<f:viewParam ... required="#{not facesContext.postback}" />
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Are you implying the `required` check is performed after the conversion? Or does the selection of the `f:selectItem` placeholder not constitute something that would violate the `required="true"` attribute? Most importantly, how should `DoctorConverter.getAsObject` handle the `value.toString().length == 0` case, which is the case for the 2nd and 3rd calls? – Steve Oct 01 '11 at 17:25
  • You should use `required="true"` if selection is required. The `noSelectionOption` just represents the default selection or an unselectable option (e.g. `--------------` separator or something). As to the empty string check, this is unnecessary when you use the mentioned context param. Otherwise, indeed check it as such (by the way, there's an `String#isEmpty()` method). – BalusC Oct 01 '11 at 17:28
  • With the changes specified in your answer, the secondary problem is solved (converter is no longer broken) and now I am back to square one with the primary problem - that once the `required` condition is unsatisfied (which essentially means validation has failed), attempting to submit what should be a valid option appears to do nothing except clear the validation message. – Steve Oct 01 '11 at 17:51
  • I can't reproduce that. Yet I'm using GF 3.1.1 with Mojarra 2.1.3 as well. Is the item value of no selection option still `#{null}`? Try removing `noSelectionOption="true"`. To recap, the converter **does** receive the right submitted value on 1st call? Are there really no other validation/conversion errors? You should see them all by `` or reading server log for missing `FacesMessage`s. – BalusC Oct 01 '11 at 17:55
  • 1
    Ah wait a minute... Adding `` reveals another message: `j_idt2: Validation Error: Value is required.` There is no j_idt2 in the client-side DOM, so I'll have to dig deeper. Might have to take the faces servlet out of production mode, which means bye bye JRebel. – Steve Oct 01 '11 at 18:04
  • 1
    You wouldn't believe it: It's the freakin' view parameter not being set for ajax calls. Of course, it's marked as required. The bug is here: [http://java.net/jira/browse/JAVASERVERFACES-1532](http://java.net/jira/browse/JAVASERVERFACES-1532) – Steve Oct 01 '11 at 18:26
  • 1
    Geez. Make it `required="#{!facesContext.postback}"` instead, if it's a viewscoped bean anyway. I haven't used view parameters in my environment, hence I never hit it. Related: http://jdevelopment.nl/stateless-stateful-jsf-view-parameters/ – BalusC Oct 01 '11 at 18:31
  • Yes! This is the answer! Please convert it to an answer and I will mark it as accepted. Btw, please check your facebook messages as I sent a question regarding your amazon.com wishlist. – Steve Oct 01 '11 at 18:39
  • I edited it into the answer. I think I will add a section about this to my last blog http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html. Btw: I haven't received any FB messages? – BalusC Oct 01 '11 at 18:50
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3952/discussion-between-steve-taylor-and-balusc) – Steve Oct 01 '11 at 18:58
  • Unbelievable. Just spent ages to find this solution! Thanks BalusC again. – Kawu Nov 01 '11 at 10:35
0

The following:

converter="#{doctorConverter}"

must be written as:

converter="doctorConverter"
Charles Menguy
  • 40,830
  • 17
  • 95
  • 117
  • 1
    Wrong. It is perfectly valid to refer to a `@javax.inject.Named` annotated converter in this way when using CDI. Please see the definition of `DoctorConverter`. Try it for yourself. – Steve Jan 05 '13 at 04:35