3
votes

I'm experiencing strange behavior with a jsf action using p:commandButton. The corresponding page takes care of some create and update tasks. So I can access the page with a blank form or continue with some already saved data which is loaded into the form. The button which is not working as expected triggers the save actions to persist the forms information.

Now here is my problem: When I continue with some already saved data and I click "save" everything works fine. The action is called and the data gets persisted or validation errors are shown. Here are some log statements I got by logging in MyPhaseListener.afterPhase(). I cut some log lines that actually confirm my data was persisted.

10:43:53,959 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,960 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE RESTORE_VIEW 1
10:43:53,960 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:53,960 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,961 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,961 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE APPLY_REQUEST_VALUES 2
10:43:53,962 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:53,962 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,963 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,964 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE PROCESS_VALIDATIONS 3
10:43:53,964 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:53,964 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,965 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:53,965 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE UPDATE_MODEL_VALUES 4
10:43:53,965 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:53,966 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:54,041 INFO  [com.egrima.cockpit.beans.toll.TollRegistrationBean] (http-localhost/127.0.0.1:8080-1) WEB: TOLL_REGISTRATION_SAVE "registrationId:210001-2783,registrationStatus:OPEN"
10:43:54,081 DEBUG [com.egrima.cockpit.beans.toll.TollRegistrationBean] (http-localhost/127.0.0.1:8080-1) Registration saved by user [email protected] and customer number 210001
10:43:54,081 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:54,082 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE INVOKE_APPLICATION 5
10:43:54,083 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:54,083 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:54,121 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:43:54,121 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE RENDER_RESPONSE 6
10:43:54,121 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) Method expression of the action being invoked: #{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}
10:43:54,121 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------

Now the stange part is when I do the save for new data there is no action called. So the goal is not to update existing data but insert new data but that difference is more interesting for my JPA layer than for the JSF bean. The lifecycle is processed normally but there is no action called.

10:51:10,813 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,813 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE RESTORE_VIEW 1
10:51:10,813 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,813 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,813 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE APPLY_REQUEST_VALUES 2
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE PROCESS_VALIDATIONS 3
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE UPDATE_MODEL_VALUES 4
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,814 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,815 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE INVOKE_APPLICATION 5
10:51:10,815 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,825 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------
10:51:10,825 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) AFTER PHASE RENDER_RESPONSE 6
10:51:10,825 INFO  [com.egrima.cockpit.server.LifeCycleListener] (http-localhost/127.0.0.1:8080-1) --------------------------------------------------------------------

Aside from invoking the action it looks quite similar. And that's what I can't get my head around. I'm clicking exactly the same button and call exactly the same action.

Here is my complete xhtml file with the h:form. I shortened it slightly. The whole file contains more input fields (they all follow the same pattern) and some additional tags which are only important for styling purposes.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:fn="http://java.sun.com/jsp/jstl/functions"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:form id="tollCustomerForm">

        <h:panelGrid columns="3" cellpadding="0" styleClass="width-100" columnClasses="width-50 valign-top, width-50 valign-top">

            <h:panelGrid columns="2" columnClasses="width-30, width-70 space-right" rendered="#{tollRegistrationBean.registration.customerData.company.visible}">
                <h:panelGroup>
                    <h:outputLabel for="inputCompany" value="#{msg.toll_registration_customerData_company}" />
                    <h:outputLabel value=" #{msg.gloabl_required_field_sign}" rendered="false" />
                </h:panelGroup>
                <h:inputText id="inputCompany" value="#{tollRegistrationBean.registration.customerData.company.value}"
                    styleClass="#{!component.valid ? 'errorInputText' : 'dkv-inputText'}" readonly="true" disabled="#{tollRegistrationBean.registrationSent}"
                    label="#{msg.toll_registration_accordion_tab_customer_data} - #{msg.toll_registration_customerData_company}" required="false" />
            </h:panelGrid>
            <h:panelGrid columns="2" columnClasses="width-30, width-70 space-right" rendered="#{tollRegistrationBean.registration.customerData.addressAddition.visible}">
                <h:panelGroup>
                    <h:outputLabel for="inputCountry" value="#{msg.toll_registration_customerData_country}" />
                    <h:outputLabel value=" #{msg.gloabl_required_field_sign}" rendered="false" />
                </h:panelGroup>
                <h:inputText id="inputCountry" value="#{tollRegistrationBean.getCountryName(tollRegistrationBean.registration.customerData.country.value)}"
                    styleClass="#{!component.valid ? 'errorInputText' : 'dkv-inputText'}" readonly="true" disabled="#{tollRegistrationBean.registrationSent}"
                    label="#{msg.toll_registration_accordion_tab_customer_data} - #{msg.toll_registration_customerData_country}" required="false" />
            </h:panelGrid>
            <h:panelGrid columns="2" columnClasses="space-left width-30, width-70" rendered="#{customerBean.renderCustomernumbers}">
                <h:panelGroup>
                    <h:outputLabel for="inputCustomerNumber" value="#{msg.toll_registration_customerData_customerNumber}" />
                    <h:outputLabel value=" #{msg.gloabl_required_field_sign}" rendered="false" />
                </h:panelGroup>
                <h:inputText id="inputCustomerNumber" value="#{customerBean.customernumber}"
                    styleClass="#{!component.valid ? 'errorInputText' : 'dkv-inputText'}" readonly="true" disabled="#{tollRegistrationBean.registrationSent}"
                    label="#{msg.toll_registration_accordion_tab_customer_data} - #{msg.toll_registration_customerData_customerNumber}" required="false" />
            </h:panelGrid>
            <h:panelGrid columns="2" columnClasses="space-left width-30, width-70" rendered="#{tollRegistrationBean.registration.customerData.email.visible}">
                <h:panelGroup>
                    <h:outputLabel for="inputEmail" value="#{msg.toll_registration_customerData_email}" />
                    <h:outputLabel value=" #{msg.gloabl_required_field_sign}" rendered="false" />
                </h:panelGroup>
                <h:inputText id="inputEmail" value="#{tollRegistrationBean.registration.customerData.email.value}"
                    styleClass="#{!component.valid ? 'errorInputText' : 'dkv-inputText'}" readonly="true" disabled="#{tollRegistrationBean.registrationSent}"
                    label="#{msg.toll_registration_accordion_tab_customer_data} - #{msg.toll_registration_customerData_email}" required="false" />
            </h:panelGrid>

        </h:panelGrid>

        <!-- ########## THE STRANGE SAVE BUTTON ########## -->
        <p:commandButton value="#{msg.toll_registration_accordion_save_step_button}" id="saveRegBtn" update=":tollAccordion :tollSelectionForm:globalMessages :tollSelectionForm:registrationProgressBar"
            action="#{tollRegistrationBean.saveRegistration('CUSTOMER_DATA')}" disabled="#{tollRegistrationBean.registrationSent}" styleClass="dkv-button" >
                <p:resetInput target=":tollAccordion:tollCustomerForm" />
        </p:commandButton>
        <!-- ########## THE STRANGE SAVE BUTTON ########## -->

        <!-- preValidation method only executed, if submit button is pressed (not called, if save-button is clicked) -->
        <h:panelGroup rendered="#{param['tollAccordion:tollCustomerForm:saveAndNextStepRegBtn']!=null}">
            <f:event listener="#{tollRegistrationBean.listenNextAccordionStep}" type="preValidate" />
            <f:attribute name="currentAccordionStep" value="CUSTOMER_DATA" />
        </h:panelGroup>

    </h:form>
</ui:composition>

This ui:composition is included in another xhtml file with layout information. This layout-template is rather unspectacular and it does not include a h:form so there are no nested forms.

and my (shortened) jsf bean

@Component
@Scope("view")
public class TollRegistrationBean implements Serializable {

    ...

    @PostConstruct
    public void init() {

        // get registrationId by param and load registration
        RequestContext context = RequestContext.getCurrentInstance();
        String registrationId = (String)context.getCallbackParams().get(TollRegistrationMainBean.CALLBACK_PARAM_SELECTED_REGISTRATION_ID);
        if (registrationId != null && !registrationId.isEmpty()) {
            registration = tollPersistingService.loadRegistrationAndCheckFields(registrationId);
        }

        if (registration != null) {
            createAccordionOrder();
            selectedTolls = registration.getTollTypes();

            // update saved customer data, read from DB, with data from crm
            tollCustomerBean.loadCustomerDataFromCRM(registration);
        }

        activeIndex = 0;
        registrationSent = false;
    }

    public void saveRegistration(String currentAccordionStep) {
        log.info("TollRegistrationBean.saveRegistration()");

        try {
            tollPersistingService.saveRegistration(registration, customerBean.getCustomernumber());
            activeIndex = accordionOrder.indexOf(currentAccordionStep);
            registration.getStepByStepName(StepName.valueOf(currentAccordionStep)).setCssAccordionIcon("");

            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_INFO, MessageUtils.getMessage("toll_registration_info_saved_successfully"), ""));

            String userEmail = UserUtils.getCurrentUser().getEmailAddress();
            log.database(AdvancedLoggerUtils.Actions.TOLL_REGISTRATION_SAVE.name(), registration.toDatabaseLogString(), userEmail,
                    AdvancedLoggerUtils.System.WEB.name());
            log.debug("Registration saved by user " + userEmail + " and customer number " + customerBean.getCustomernumber());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}

I'm using JSF 2.2, Primefaces 5.2 and Spring 4.1.4.RELEASE.

Has anyone expirienced the same or a similar issue? I'd be really grateful for any help or hint. I'm kinda stuck with this.

Thanks and regards Sebastian

1
And you are using Spring... why not add that to the tags?Kukeltje
Could you append the entire form to your post?javahippie
I updated the spring information and the tag above (thanks for the hint) and posted my xhtml file below.Sebastian Sommerfeld

1 Answers

2
votes

Good morning Sebastian,

the form can not be submited if p:remoteCommand is still running. You have to place it in a separate form ...

Best regards