jsfcdiconversation-scope

ConversationScoped bean's PostConstruct method called in every request


Well, i'm using a ConversationScoped and i hope the PostConstruct is called just one time in begin of conversation, see:

@Named("disciplinaDetalheMB")
@ConversationScoped
public class DisciplinaDetalheMBImpl {

    private static final long serialVersionUID = 1L;

    @Inject
        private Conversation conversation;

    @Inject
    @AnBasicBO
    private BasicBO boPadrao;

@PostConstruct
    public void postConstruct() {
    logger.debug("Iniciando PostConstruct...");
    init();
    beginConversation();
    }

public String salvarAndRedirecionar() {
    salvar();
    if (!FacesContext.getCurrentInstance().isValidationFailed()) {
        return goToLastPage() + "?faces-redirect=true";
    } else {
        return "";
    }
    }


private void beginConversation() {
    if (!conversation.isTransient()) {
        endConversation();
    }
    conversation.begin();
    if (conversation.isTransient()) {
        throw new RuntimeException("A conversão não foi iniciada corretamente");
    }
    SessionContext.getInstance().setAttribute("cid", conversation.getId());

    }

    public BasicBO getBoPadrao() {
        return boPadrao;
    }

    public void setBoPadrao(BasicBO boPadrao) {
        this.boPadrao = boPadrao;
    }

}

So, when my backing bean is created, the conversation is initialized and CID is stored in session to be used after. I have a commandButton "save" in my XHTML and when this button is called the PostConstruct is called again i don't know why:

<h:commandLink
            action="#{managedBeanName.salvarAndRedirecionar()}"
            styleClass="btn btn-info pull-right" value="Salvar">
            <f:ajax execute="@form" />
        </h:commandLink>

I noted the generated HTML is:

<a id="formManterDisciplina:j_idt44:j_idt46" href="#" onclick="mojarra.ab(this,event,'action','@form',0);return false" class="btn btn-info pull-right" name="formManterDisciplina:j_idt44:j_idt46">Salvar</a>

So, I understand the "href=#" avoid the onlick to be executed. I think this is the problem but i dont't know how to fix. Remenber: The salvarAndRedirecionar() method is never called because postConstruct is always called before.

2) I have another question: If i start a conversation and don't end, there is some problem ? Sometimes i don't want to end conversation manually because i just have ONE PAGE, i just start.


Solution

  • The reason why you are having this issue is because you are invoking the conversation begin method in the postconstruct method of the conversation scoped bean. So, the conversation will be set to long-running state during the Render Response phase and not before it. The problem is that the CID parameter is rendered on the HTML form element, but at this point the conversation is still in transient state, because the postconstruct method has not still been invoked after the request. The postconstruct method is invoked when redenring the commandLink element, and then is too late, and the HTML form element won't carry the CID parameter:

    <form id="yourForm" name="yourForm" method="post" action="/path/to/yourPage.xhtml" enctype="application/x-www-form-urlencoded">

    So, the solution consists of moving the conversation begin to a point before the Render Response phase. You can do it with the f:viewAction tag if you are using JSF 2.2 or with the f:event tag if you are using an older version.

    And then you will see the CID parameter rendered inside your HTML form element, like so:

    <form id="yourForm" name="yourForm" method="post" action="/path/to/yourPage.xhtml?cid=1" enctype="application/x-www-form-urlencoded">

    In your page:

    <f:metadata>
        <f:event listener="#{disciplinaDetalheMB.initConversation}" type="preRenderView" /> 
    </f:metadata>
    

    In your backing bean:

    public void initConversation(){
        if (!FacesContext.getCurrentInstance().isPostback() && conversation.isTransient()) {
              conversation.begin();
        }
    }
    

    In your page:

    <f:metadata>
        <f:viewAction action="#{disciplinaDetalheMB.initConversation}" />
    </f:metadata>
    

    In your backing bean:

    public void initConversation(){
        if (conversation.isTransient()) {
              conversation.begin();
        }
    }
    

    Regarding your second question there is not big problem in not ending a conversation, because it has a timeout like a HTTP session. You can set the timeout value depending on your server resource management strategy and the desired lifetime for an idle conversation. Anyway, when you only have one page you better use a view scoped backing bean.