Search code examples
jsfjakarta-eejsf-2primefacescdi

Managed Been not updated with up to date information on second submit


I am having a problem here that seem to have the better of me.

Context

I have a p:dialog (primefaces) that show details of a JPA entity. From this dialog, the user can modify the information and submit it. Once submitted, the information is saved in DB (through JPA).

The p:dialog is "wrapped" in a Component for use in different situations.

Problem:

If I show the dialog with an entity selected from DB, modify some information and click on the save (submit) button: it works fine the first time. The DB is updated and p:dialog is hidden.
If I show again the dialog with the same entity, modify again a data from the p:dialog and submit again: everything seems to work fine (confirmation messages & logs) but the DB is not updated.

At all time, the information found in the p:dialog is right. The data found in the BackingBean & DB though, are up to date only after the first submit.

Another clue: if after a submit action I refresh the page, it will work for one more submit.

Debugging information:

According to the information Posted by the submit (long life to Firebug!!!) all the data posted are right (always up to date). But if I display in the log what the backing bean receives, it is the same content as the previous submit (and not the new information).

The data is posted correctly, but seems to be badly received/interpreted. It is just like if the problem was occurring at the reception of the submit's post, in one of the RestoreView, ApplyRequestValue, ProcessValidation or UpdateModelValue phases of the JSF life cycle.

So, the reason why the second save (submit) seems to work but doesn't is because the data saved in the DB is the same for every subsequent submit..... Why?

I use Glassfish 3.1.2, Mojarra 2.1.13, JSF, PrimeFaces, CDI, JPA, Hibernate...

Code Snipets:

page including the p:dialog (cmpnt:dataEntryDialog) as Component:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:p="http://primefaces.org/ui"
        xmlns:cmpnt="http://java.sun.com/jsf/composite/component"
>
    <h:head>
        <title>
            Test!!!
        </title>
    </h:head>
    <h:body>
        <h:commandButton type="button" onclick="PVDlg.show();" value="show dlg"/>
        <cmpnt:dataEntryDialog id="PVDataEntry" video="#{processStatus.testedVideo}" fieldGroupId="2" 
                header="This is a test"
                widgetVar="PVDlg" render="@this"/>
    </h:body>
</html>

p:dialog's component implementation (cmpnt:dataEntryDialog):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:composite="http://java.sun.com/jsf/composite"
        xmlns:c="http://java.sun.com/jsp/jstl/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:p="http://primefaces.org/ui">

    <composite:interface componentType="dataEntryComponent">
        <composite:attribute name="video" required="true"/>
        <composite:attribute name="widgetVar" required="true"/>
        <composite:attribute name="render" required="false"/>
        <composite:attribute name="closeActionListener" method-signature="void listener(org.primefaces.event.CloseEvent)" required="false" />
        [... other attributes]
    </composite:interface>
    
    <composite:implementation>
        <h:outputStylesheet library="css" name="dataEntryDialog.css" target="head" />

        <div id="#{cc.clientId}">
            <script type="text/javascript">
                function handleDataEntrySaveAttempt(xhr, status, args) {
                    if( args.validationFailed || !args.saved ) {
                        jQuery('#' + #{cc.clientId} + ':cmpntDataEntryDialog').effect("shake", {times:3}, 100);
                    } else {
                        ${cc.attrs.widgetVar}.hide();
                    }
                    return true;
                }
            </script>
            <p:dialog 
                    id="cmpntDataEntryDialog" 
                    header="#{cc.attrs.header}"
                    widgetVar="#{cc.attrs.widgetVar}"
                    resizable="false"
                    showEffect="fade"
                    hideEffect="fade"
                    dynamic="true"
                    minimizable="true"
                    maximizable="false"
                    width="550" 
                    height="500"
            >
                <h:form id="cmpntDataEntryForm" style="position:relative;">
                    <div style="height:460px;">
                        <div style="width:100%;height:100%;margin-bottom:5px;overflow:auto;">
                            <ui:repeat value="#{dataEntryDialog.loadDataEntryFields( cc.attrs.video, cc.attrs.fieldGroupId )}" var="field" varStatus="fieldRankInfo">
                                <div style="position:relative;width:100%;height:24px;margin:4px 0px;">
                                    <h:outputText value="#{field.fieldName}:" style="vertical-align:middle;" styleClass="margin"/>
                                    <p:calendar id="cmpntInputDate" value="#{field.value}" rendered="#{'java.util.Date' eq field.type}" styleClass="input margin" effect="drop" />
                                    
                                    <p:selectOneMenu id="cmpntInputComboBox" value="#{field.value}" rendered="#{'ComboBox' eq field.type}" styleClass="input margin">
                                        <f:selectItem itemLabel="SelectAValue"/>
                                        <f:selectItems value="#{field.possibleValues}"/>
                                        <f:converter converterId="com.ctec.world.ConvertInteger" />
                                    </p:selectOneMenu>

                                    [... some other optionally rendered fields for different data types]

                                </div>
                            </ui:repeat>
                        </div>
                    </div>
                    <div style="position:relative;bottom:0;">
                        <div style="float:right;margin-top:5px;">
                            <p:commandButton id="cmpntSubmit" action="#{dataEntryDialog.save( cc.attrs.video.video )}"
                                    value="${cc.resourceBundleMap.dataEntryDialog_Save}"
                                    process="@form" update="${cc.attrs.render} @form"
                                    styleClass="margin" oncomplete="handleDataEntrySaveAttempt( xhr, status, args )"/>
                            <p:commandButton id="cmpntCancel"
                                    value="${cc.resourceBundleMap.dataEntryDialog_Cancel}" 
                                    onclick="${cc.attrs.widgetVar}.hide();" styleClass="margin"/>
                        </div>
                    </div>
                </h:form>
            </p:dialog>
        </div>
    </composite:implementation>
</html>

Concerning the Managed beans, they are plain CDI @Named @SessionScoped bean.

ADDITIONAL INFORMATION:

I made further tests: went through phase listener to see if there is any object accessible from there having interesting information... no luck here until now.


Solution

  • I've got it! The problem is not on the presentation layer (what a surprise!). It is related on my logic. A little while ago, I have fixed an JPA Optimistic Lock Exception by changing entity instances (no longer managed by the entityManager) with the managed instance returned by the entity manager's merge method. My design wasn't supporting that change everywhere as expected... and I got lured by the post content which was right but not going in the entity instance I was expecting it to be. I guess more experience in web development won't hurt!