Samstag, 1. August 2015

Heroic alternatives to avoid the annoying ADF SessionTimout Popups

Tech stack

Implementation demonstrated in this blog is developed/tested using JDeveloper version 11.1.1.9.0 and version 12.1.3

Use case

We all know the ADF popup, that pops up, after the Session timed out. I had quite a few customers, that were asking me, if they can change the text of the popup, forward to some specific page, after clicking OK or even automatically getting forwarded after the session timed out. All of them didn't really like the standard ADF popup, which also doesn't support accessibility.

Our goal in this blog is to get rid of this


and instead automatically forward to a nice looking page like this, after session timeout



Implementation steps

Step 1:  We set the session timeout to 1, while we are implementing this, so we don't have to wait 35 minutes for the session timeout. Don't forget to set this back to your old value, after you are done with the implementation.

 
1




Step 2:  At first we want to get rid of the ADF session timeout popup. We do this by making adjustments to the web.xml.


 
    javax.faces.STATE_SAVING_METHOD
    client
  
  
    oracle.adf.view.rich.sessionHandling.WARNING_BEFORE_TIMEOUT
    0
  
  

Step 3 : Now that we got rid of the default ADF popup, we want to create our own sessionTimeout.jspx file. This will be just a standard jspx file, that we will forward to after the session timed out.


  
  
    
      
        
      
    
  



Step 4 Now that we did find a solution on how to hide the popup and we also created our own custom sessionTimeout.jspx file there is one big question. How do we forward to this page after the session timed out??? I was looking around in the world wide web and found a really nice blog, which had a great solution.

http://www.javaworld.com/article/2073234/tracking-session-expiration-in-browser.html

So our task in step 4 is reading the blog to understand how the forward is working. If you are feeling like an expert, you can skip reading the blog :-)

In short I can tell you this. A servlet filter is attaching to each response a cookie with a timestamp, telling when the session will expire. On Javascript side we read this information each second from the cookie and calculate, if the session already timed out. If the session timed out, we will forward to the sessionTimeout.jspx. So let's begin.

Step 4.1 Create your own custom servlet filter, which is reading the sessionExpirationTime and the serverTime and attaching it to the httpResponse as cookie.
public class SessionTimeoutFilter implements Filter {
  public SessionTimeoutFilter() {
    super();
  }

  public void init(FilterConfig filterConfig) {
  }

    /**
     * Custom filter that is writing the current serverTime and the sessionExpiry time to a cookie
     * The current time is needed to calculate the offset between server and client time
     * 
     * @param req
     * @param resp
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
    HttpServletResponse httpResp = (HttpServletResponse)resp;
    HttpServletRequest httpReq = (HttpServletRequest)req;
    long currTime = System.currentTimeMillis();
    long expiryTime = currTime + httpReq.getSession().getMaxInactiveInterval() * 1000;
    Cookie cookie = new Cookie("serverTime", "" + currTime);
    cookie.setPath("/");
    httpResp.addCookie(cookie);
    cookie = new Cookie("sessionExpiry", "" + expiryTime);
    cookie.setPath("/");
    httpResp.addCookie(cookie);
    filterChain.doFilter(req, resp);
  }

  public void destroy() {
  }
}


Step 4.2 In the web.xml we have to register our custom filter.
  
    SessionTimeoutFilter
    com.guardiansoftheoracleworld.adfman.blog.view.SessionTimeoutFilter
  
  
    SessionTimeoutFilter
    /*
  

Step 4.3 Create a Javascript function, which is reading the sessionExpiration each second and checking, if the session already timed out. If the session timed out, a forward will be made to our custom sessionTimeout.jspx file. The function calling itself is made recursevly with the setTimeout function.
This Javascript code has also some not explained advanced features, like a counter or a message telling, that the session timed out and a forward will be made in five seconds.
/**
 * Check periodically each second, if the session has timed out
 */
function checkSession() {
  var outputText = AdfPage.PAGE.findComponentByAbsoluteId("ot3");
    
  var sessionExpiry = Math.abs(getCookie('sessionExpiry'));
  var timeOffset = Math.abs(getCookie('clientTimeOffset'));
  var localTime = (new Date()).getTime();
  if (localTime - timeOffset > (sessionExpiry + 5000)) {
    // 5 extra seconds to make sure
    window.location = "/TimeoutSessionFilter-ViewController-context-root/faces/sessionTimeout.jspx";
  } else if (localTime - timeOffset > sessionExpiry) {
    outputText.setValue('Session timed out. Forward in five seconds !!!');  
    setTimeout('checkSession()', 1000);
  }
  else {
    // calculate remaining time in seconds to show it to the client
    var remainingTime = Math.round((sessionExpiry - (localTime - timeOffset)) / 1000);
    outputText.setValue('remainingTime: ' + remainingTime + ', timeOffset: ' + timeOffset);
    setTimeout('checkSession()', 1000);
  }
}

Step 4.4 The last step is, that we have to call the checkSession() function once to trigger this whole thing. We can do this with an ADF clientListener.
      


Summary

This is a quite good alternative to the ADF popup and should also be compatible with future ADF versions, since it hasn't too much to do with ADF itself.
Download the demo to take a look at the whole source code, which contains also some stuff, that is not explained here or just take a look at the video demonstration, in which the result is being presented.

Video Demo: https://www.youtube.com/watch?v=6GA49ONnG3o

Source Code: http://www.guardiansoftheoracleworld.com/blogs/adfman/2015/20150801_session_timeout_forward.zip

4 Kommentare:

  1. Some of the implementation steps are now shown in the blog.

    AntwortenLöschen
  2. Hi Thorsten.

    Thx for the feedback.

    This is true. The blog is only describing the basic idea of how to do this. Checkout the source code which is commented in some parts to find out more detailed stuff about the implementation.

    Or you can always ask me, if you have a question.

    Greetings
    ADF man

    AntwortenLöschen
  3. What I mean was that your code blocks was not correctly displayed, today it works

    AntwortenLöschen