Referencing processScope values for Back button compatibility

Events initiated by JSF Components placed inside a switcher tag might be overlooked by the framework, if the scope of the value referenced in the facetName attribute of the switcher is not set correctly.

In this post, we are going to demonstrate how to use the ADF processScope to solve this problem.

You can download the sample application from here. The application is a JDeveloper 10.1.3.2 workspace. It does not need a database connection to run.

Sample Application

The sample application contains 3 pages. Each page attempts to perform the same tasks.

“Button 1” is displayed when the application is run the first time. Clicking “Button 1” hides it and displays “Button 2” with a timestamp (System.currentTimeInMilis()) next to it, indicating the time the “Button 1” is pressed.

Pressing “Button 2” performs a similar action; “Button 2” is hidden and only a timestamp is displayed, showing the time “Button 2” is pressed.


All three pages utilize the same bean, named MyBean, to provide their functionality.
import javax.faces.event.ActionEvent;

public class MyBean
{
public MyBean()
{
}
Object screenState = 0;
String clickDate;

public void doButton1Click(ActionEvent actionEvent)
{
String text = Long.toString(System.currentTimeMillis());
System.out.println("button 1 clicked at:" + text);
this.setClickDate(text);
}

public void doButton2Click(ActionEvent actionEvent)
{
String text = Long.toString(System.currentTimeMillis());
System.out.println("button 2 clicked at:" + text);
this.setClickDate(text);
}

public void setClickDate(String clickDate)
{
this.clickDate = clickDate;
}

public String getClickDate()
{
return clickDate;
}

public void setScreenState(Object screenState)
{
this.screenState = screenState;
}

public Object getScreenState()
{
return screenState;
}
}
The three pages differ only in the scope of the value referenced in the facetName attribute of the switcher tag, which is used to determine which commandButton should be displayed.

Request Scope Page
<af:switcher facetName="#{MyBeanRequestScopeInstance.screenState}">
<f:facet name="0">
<af:commandButton text="Button 1"
actionListener="#{MyBeanRequestScopeInstance.doButton1Click}">
<af:setActionListener from="#{MyBeanRequestScopeInstance.screenState+1}"
to="#{MyBeanRequestScopeInstance.screenState}"/>
</af:commandButton>
</f:facet>
<f:facet name="1">
<af:commandButton text="Button 2"
actionListener="#{MyBeanRequestScopeInstance.doButton2Click}">
<af:setActionListener from="#{MyBeanRequestScopeInstance.screenState+1}"
to="#{MyBeanRequestScopeInstance.screenState}"/>
</af:commandButton>
</f:facet>
</af:switcher>
<af:outputLabel value="#{MyBeanRequestScopeInstance.clickDate}"/>
In this page, the switcher tag uses a request scope instance of MyBean. The value for the facetName attribute is bound to the screenState property of the bean. Clicking “Button 1” works correctly and renders “Button 2” with a timestamp next to it. However, clicking “Button 2”, does not execute its actionListener. The application renders “Button 1” which is not the desired result.

The MyBean instance used to render “Button 2” was discarded once the request was completed. Clicking “Button 2” invokes a new request. Therefore a new instance of MyBean is created. This resets the value of its screenState attribute to "0". Based on this value of screenState, the framework erroneously concludes that “Button 2” was not rendered to the user agent. Consequently, any events initiated by “Button 2” are not detected by the framework.

A step by step explanation of why this implementation does not work can be found here

The post in this URL discusses the rendered attribute of JSF Components. However, the same principles can be applied in this context as well.

Session Scope Page
<af:switcher facetName="#{MyBeanSessionScopeInstance.screenState}">
<f:facet name="0">
<af:commandButton text="Button 1"
actionListener="#{MyBeanSessionScopeInstance.doButton1Click}">
<af:setActionListener from="#{MyBeanSessionScopeInstance.screenState+1}"
to="#{MyBeanSessionScopeInstance.screenState}"/>
</af:commandButton>
</f:facet>
<f:facet name="1">
<af:commandButton text="Button 2"
actionListener="#{MyBeanSessionScopeInstance.doButton2Click}">
<af:setActionListener from="#{MyBeanSessionScopeInstance.screenState+1}"
to="#{MyBeanSessionScopeInstance.screenState}"/>
</af:commandButton>
</f:facet>
</af:switcher>
<af:outputLabel value="#{MyBeanSessionScopeInstance.clickDate}"/>
In this page, the switcher tag references a session scope instance of MyBean. This page performs the tasks described earlier in this post successfully, unless the browser’s Back button is clicked. Neither “Button 1”, nor “Button 2” works after clicking the browser’s Back button.

Clicking “Button 1” renders “Button 2” with a timestamp showing T1. Clicking the Back button and then pressing “Button 1” once more, renders “Button 2” again which seems like the correct behavior at first. However, the timestamp next to the “Button 2” is still showing the value T1. If the actionListener of “Button 1” executed we would have seen a different value.

Clicking the Back button does not update the screenState in the session scope instance of MyBean. Identical to the problem with the request scope page, the screen the user actually sees, and the framework thinks the user sees, are not the same after the user clicks the Back button. Therefore, the button press event that occurs after using the Back button is not recognized by the framework.

Process Scope Page
<af:switcher facetName="#{processScope.screenState==null ? 0 : processScope.screenState}">
<f:facet name="0">
<af:commandButton text="Button 1"
actionListener="#{MyBeanSessionScopeInstance.doButton1Click}">
<af:setActionListener from="#{processScope.screenState+1}"
to="#{processScope.screenState}"/>
</af:commandButton>
</f:facet>
<f:facet name="1">
<af:commandButton text="Button 2"
actionListener="#{MyBeanSessionScopeInstance.doButton2Click}">
<af:setActionListener from="#{processScope.screenState+1}"
to="#{processScope.screenState}"/>
</af:commandButton>
</f:facet>
<f:facet name="2"></f:facet>
</af:switcher>
<af:outputLabel value="#{MyBeanSessionScopeInstance.clickDate}"/>
In this page, the switcher’s facetName attribute references the screenState value which is placed in the processScope. Oracle JDeveloper online documentation states that “clicking on the browser's Back button automatically resets processScope to its original state”. Accordingly, the buttons on this page work correctly even after the Back button is clicked.

Yalım K. Gerger

Comments

Popular posts from this blog

Monitoring Oracle Database with Zabbix

Powerful Free Webinar Network for Oracle Developers