Tuesday, June 24, 2014

ADF : Handling browser back button

One of the most annoying side effects of doing a PPR request or matter of fact an AJAX request is the inability of the user to go back to previous transaction using browser back button. This is a common pitfall and there are number of ways to deal with it.

Case Study:
Lets assume we have 3 page fragments (jsff) in a bounded task flow and "main.jspx" where task flow is imported and a "launch.jspx" from which we trigger the new page with TF. When the user goes from step 1 to step 3 through step 2 and does a back button, the user is redirected to "launch.jspx" instead of "main.jspx" ( step 2)

Ways to handle browser back button

  1. Simplest - Avoid using bounded task flow ( Not recommended ) -  Now ! this may sound as a non - solution and to most part of it is correct ! but for specific user stories it could make sense. For example, in the above scenario we could use 3 different task flows for each of the fragments and use af:goLinks to navigate from one page to the other by keeping the data in session. This could be a solution when reusing task flow is not really a concern.
  2. Using session variable and servlet filter - We create a bounded task flow with session variable (currentStep = 0) as the input to each step, this is to track the user's current step. 
    • If the user is in step 3 then we set currentStep = 3 and set the value in session. 
    • When the user clicks on back button we check if the request is coming from "launch.jspx" and the currentStep != 0 then go to "main.jspx" with currentStep = currentStep  - 1
    • Once we have the Task Flow triggered through "main.jspx" - we check the value of currentStep  and show the user the requested fragment
  3. Using session variable and PagePhaseListener  ( Recommended )- We follow the same steps as above but instead of a filter we use a PagePhaseListener  to capture the back button request to "launch.jspx".
  4. There is an excellent article on "How To Handle Web Browser Buttons in ADF/WebCenter Applications" by Andrejus who provides an additional approach.

Preventing request caching on browser back button

One more catch with the above implementation logic is to get the request to come to server when user clicks on back button as browser tend to cache the resources.

To prevent it browser caching:

package com.example;  
 import javax.faces.context.FacesContext;  
 import javax.faces.event.PhaseEvent;  
 import javax.faces.event.PhaseId;  
 import javax.faces.event.PhaseListener;  
 import javax.servlet.http.HttpServletResponse;  
 public class PagePhasehaseListener implements PhaseListener  
   public PhaseId getPhaseId()  
     return PhaseId.RENDER_RESPONSE;  
   public void afterPhase(PhaseEvent event)  
   public void beforePhase(PhaseEvent event)  
     FacesContext facesContext = event.getFacesContext();  
     HttpServletResponse response = (HttpServletResponse) facesContext  
     response.addHeader("Pragma", "no-cache");  
     response.addHeader("Expires", "Wed, 1 Dec 2012 01:00:00 GMT");  
     response.addHeader("Cache-Control", "no-cache");  
     response.addHeader("Cache-Control", "no-store");  
     response.addHeader("Cache-Control", "must-revalidate");