ADF Listeners - Part III

ADF Application Event Listeners, QueryString Parameters reinjection between sessions – Part III

In ADF applications as any web application you can make use of QueryString parameters to perform custom actions inside of your application. But what happens to your QueryString parameters when the current session expires? In ADF they are lost between sessions. So, how can we reinject the QueryStrings parameters in the new session? You have two options:

  • You can manually call again the application with the same QueryString parameters. But what if you’re using your application inside of another? Probably this option may not work.
  • You can try to catch the session timeout. But you need to catch the session timeout before it actually expires otherwise you lost the QueryString parameters. How to do it?

The answer to the previous question resides on the ADF application Events Listeners. In my previous posts you can find details about ADF application Events Listeners. Take a look here.

In this post I will show you how to accomplish this task.

 

Solution

In order to gather the current session QueryString parameters and reinject them in the newest one before session timeout occur you need to create the following custom listeners:

  • Custom Phase Listener
  • Custom Filter

Inside of the custom phase listener class we will be listening the lifecycle phases of our application, gather the QueryString parameters and save them in a cookie so we are able to access them in the future when the session timeout happens. The following code explains how to do it.

package view.listeners.session;

import java.util.Map;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class CustomPhaseListener implements PhaseListener{
    public CustomPhaseListener() {
        super();
    }

    public void afterPhase(PhaseEvent phaseEvent) {
        System.out.println("CustomPhaseListener");
    }

    public void beforePhase(PhaseEvent phaseEvent) {
        System.out.println("CustomPhaseListener");
        FacesContext vFacesContext = phaseEvent.getFacesContext();
        ExternalContext vExternalContext = vFacesContext.getExternalContext();
        HttpSession session = (HttpSession) vExternalContext.getSession(false);
        boolean newSession = (session == null) || (session.isNew());
        boolean postback = !vExternalContext.getRequestParameterMap().isEmpty();
        boolean timedout = postback && newSession;
        
        if (!timedout)
        {
            //Returns all QueryString paramters but you are only interested in 
            //yours parameters.
            Map<String, Object[]> queryStringMap = ((HttpServletRequest)vExternalContext.getRequest()).getParameterMap();
            
            //This utility class detectes which QueryString parameters are 
            //generated  by the ADF or have been created by you.
            QueryStringUtils qsConstants = new QueryStringUtils();
            //Returns a string with all QueryString parameters created by you.
            String queryString = qsConstants.getStringQueryStringParameters(queryStringMap);
            
            if (queryString != null && !queryString.isEmpty() &&
                !vExternalContext.getRequestCookieMap().containsKey(CookieConstants.COOKIE_QUERY_STRING_NAME))
            {
                //Inserts the QueryString parameters in a COOKIE.
                HttpServletResponse vResponse = (HttpServletResponse)vExternalContext.getResponse();
                Cookie cookie = new Cookie(CookieConstants.COOKIE_QUERY_STRING_NAME, queryString);
                vResponse.addCookie(cookie);
            }
        }
    }

    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }
}

You can find the code for the QueryStringUtils here:

package view.listeners.session;

import java.net.URLDecoder;
import java.net.URLEncoder;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class QueryStringUtils {
    
    //Example of QueryString parameters that the application accepts.
    public static final String QUERY_STRING_1 = "_qs1";
    public static final String QUERY_STRING_2 = "_qs2";
    
    private List listOfQueryStringConstants;

    public QueryStringUtils(){
        listOfQueryStringConstants = new ArrayList();
        listOfQueryStringConstants.add(QUERY_STRING_1);
        listOfQueryStringConstants.add(QUERY_STRING_2);
    }
    
    public List getQueryStringConstantsNames() {
        return listOfQueryStringConstants;
    }
    
    public Hashtable getHashQueryStringParameters(Map<String, Object[]> queryStringParams) {
        Hashtable result = new Hashtable();
        
        for(Entry<String, Object[]> entry : queryStringParams.entrySet()) {
              if (getQueryStringConstantsNames().contains(entry.getKey())) 
              {
                  Object propertyValue = entry.getValue()[0];
                  if (propertyValue != null)
                  {
                      try
                      {
                          String propertyValueAux = URLDecoder.decode(propertyValue.toString(), "UTF-8");
                          result.put(entry.getKey(), propertyValueAux);
                      }
                      catch(Exception ex) {
                          ex.printStackTrace();
                      }
                  }    
              }
        }
        
        return result;
    }

    public String getStringQueryStringParameters(Map<String, Object[]> queryStringParams) {
        String result = "";
        int i = 0;
        
        for(Entry<String, Object[]> entry : queryStringParams.entrySet()) {
              if (getQueryStringConstantsNames().contains(entry.getKey())) 
              {
                  Object propertyValue = entry.getValue()[0];
                  if (propertyValue != null)
                  {
                      try
                      {
                          String propertyValueAux = URLEncoder.encode(propertyValue.toString(), "UTF-8");
                          if (i == 0)
                            result += entry.getKey() + "=" + propertyValueAux;
                          else
                            result += "&" + entry.getKey() + "=" + propertyValueAux;
                          i ++;
                      }
                      catch(Exception ex) {
                          ex.printStackTrace();
                      }
                  }    
              }
        }
        
        return result;
    }
}

In the class CookieConstants you have:

package view.listeners.session;

public class CookieConstants {
    
    public static final String COOKIE_QUERY_STRING_NAME = "QUERY_STRING_COOKIE_EXAMPLE";
}

In the custom filter we will create a custom ServletRequest so we can insert the QueryString parameters for each new request. When the session timeout happens the custom filter is invoked and the QueryString parameters saved in the cookie are read and inserted in the request of the newest session created. Here is the code for the custom filter:

package view.listeners.session;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;


public class CustomFilter implements Filter{
    
    private FilterConfig _filterConfig = null;
    
    public void init(FilterConfig filterConfig) {
        _filterConfig = filterConfig;
    }

    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws java.io.IOException, 
                                                         javax.servlet.ServletException{
        CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest)servletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    public void destroy() {
        _filterConfig = null;
    }
}

In the custom ServletRequest we will manipulate the request data and reinject the QueryString parameters. The code for the custom ServletRequest is presented next:

package view.listeners.session;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper{
    
    private Hashtable<String,String> queryStringParameters;
    
    public CustomHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        setHashQueryStringParameters();
    }

    private String getCookieQueryStringParameters() {
        String result = null;
        Cookie[] cookie = super.getCookies();
        
        for(int i = 0; i < cookie.length; i++) {
            if (cookie[i].getName().equalsIgnoreCase(CookieConstants.COOKIE_QUERY_STRING_NAME)) {
                result = cookie[i].getValue();
            }
        }
        
        return result;
    }

    private void setHashQueryStringParameters() {
        queryStringParameters = new Hashtable<String, String>();
        String parameters = getCookieQueryStringParameters();
        
        if (parameters != null && !parameters.isEmpty()) 
        {
            String[] splitParameters = parameters.split("&");
            
            for (int j = 0; j < splitParameters.length; j++)
            {
                String[] keyValue = splitParameters[j].split("=");
                queryStringParameters.put(keyValue[0], keyValue[1]);
            }
        }
    }

    public ArrayList getQueryStringParametersNames() {
        return new ArrayList<String>(queryStringParameters.keySet());
    }

    @Override
    public Map getParameterMap() {
        return super.getParameterMap();
    }

    @Override
    public Enumeration getParameterNames() {
        Enumeration enumeration = super.getParameterNames();
        
        if(queryStringParameters.size() != 0){
            //Custom Enumeration to set the default ADF QueryString parameters 
            //and our custom parameters.
            CustomEnumeration myEnumeration = new CustomEnumeration(enumeration, getQueryStringParametersNames());
            enumeration = myEnumeration.getEnumeration();
        }
        return enumeration;
    }

    @Override
    public String[] getParameterValues(String string) {
        String[] result = super.getParameterValues(string);
        
        if (queryStringParameters.size() != 0 && queryStringParameters.containsKey(string)){
            result = new String[1];
            result[0] = queryStringParameters.get(string);
        }
        
        return result;
    }

    @Override
    public String getParameter(String string) {
        String result = super.getParameter(string);
        
        if (queryStringParameters.containsKey(string))
            result = queryStringParameters.get(string);
        
        return result;
    }
}

The code for the CustomEnumeration class can be found here:

package view.listeners.session;

import java.util.ArrayList;
import java.util.Enumeration;

import weblogic.utils.enumerations.IteratorEnumerator;

public class CustomEnumeration {
    private Enumeration enumeration;
    private ArrayList<String> queryStringParametersNames;
    
    public CustomEnumeration(Enumeration enumeration, ArrayList<String> queryStringParametersNames) {
        this.enumeration = enumeration;
        this.queryStringParametersNames = queryStringParametersNames;
    }
    
    public Enumeration getEnumeration() {
        ArrayList<String> arrayList = new ArrayList<String>(); 
        while (enumeration.hasMoreElements()) {
            arrayList.add((String)enumeration.nextElement());
        }
        
        arrayList = addqueryStringParameters(arrayList);
        
        Enumeration result = new IteratorEnumerator(arrayList.iterator()); 
        return result;
    }
    
    private ArrayList<String> addqueryStringParameters(ArrayList<String> arrayList) {
        
        for (int i = 0; i < queryStringParametersNames.size(); i++) {
            String value = queryStringParametersNames.get(i);
            if (!arrayList.contains(value))
                arrayList.add(value);
        }
        return arrayList;
    }
}

Conlusion

By intercepting the application lifecycle phases and the page requests using a custom phase listener and a custom filter we have been able to reinject QueryString parameters between sessions.

 

Cheers,

Pedro Gabriel

 

@PedrohnGabriel

Post original image by Fe Ilya

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *