Mittwoch, 26. August 2015

Using ExecuteWithParams action

Tech stack

Implementation demonstrated in this blog is developed using JDeveloper – version 11.1.1.7.0

Use case

In this blog we are going to see how we can use the often overlooked out-of-the-box action called ExecuteWithParams. This action basically lists all the bind variables associated with your view object and you can directly pass the values to these variables form the UI action binding to filter the VO. This saves you the time of writing the trivial AM method and calling it from a bean.

To illustrate the use of this action, we will create a query based view object which will have a bind variable in its query. On the UI, we will use single instance of this VO to show different data by sending different values for the bind variable.

Implementation steps

Step 1: Create EmployeeForDeptVO which will be our query based view object.



As you can see, the VO query has a bind variable pDeptId in the where clause. Expose this VO in the application module.



That’s pretty much our model side code. Let’s see how our UI is to be implemented.

Step 2: Create a jspx page with af:panelTabbed layout having 5 tabs, one each for departments: Administration, Meeting, Purchasing, Human Resources and Shipping. Upon running the page it will look like following:



Step 3: Now we will create a task flow which will look like following:



As can be seen above, the task flow has an input parameter deptId which the task flow consumer will pass. The default activity of this task flow is the ExecuteWithParams action which we drag and drop from data controls. Before adding the action to page, a dialog will ask us to provide values for all bind variables. In our case, the bind variable is pDeptId, for which we will provide #{pageFlowScope.deptId} as the value.



So what we did here is, we created a task flow which takes department Id as input parameter and pass this input value to ExecuteWithParams. Whenever this task flow is executed, the default activity ExecuteWithParams will trigger a VO query with input department Id and populate the rows accordingly.

The next activity in the task flow is a jsff page where we simply create a read only af:table using the EmployeeForDeptVO1 instance from data controls. This will load the rows of employees populated by the default activity.

Step 4: In the jspx page we created in step 3, add the above task flow inside each of the tabs (af:showDetailItem). Since there are 5 tabs, it will have 5 task flow bindings in the page definition file of the jspx page (one instance belonging to each tab).



Select each binding and pass the department Id as per the tab in which it belongs.



The department Id values will be: Administration = 10, Marketing = 20, Purchasing = 30, Human Resources = 40 and Shipping = 50.

We are now done with the UI with one small thing to do.

Step 5: If you run the page now, you will see that all the tabs displays same data. Any idea why this is happening?

This is happening because when the page is being loaded, all the tabs are trying to execute same task flow. But since we are using single VO, the tab for which query gets executed last decides what data will display in all the other tabs. In other words, if query for Administration tab is executed last then all the tabs will display employees for Administration department. How can we fix this?

The answer is to make the execution of task flow instances for each tab conditional. That is, we would want the task flow instance belonging to Marketing tab to get executed only when we click on Marketing tab. Similarly, for every individual tab, their respective task flow should get executed only when the particular tab is clicked.

In order to achieve this, we will do following:

Step 5.1 For each individual tab (af:showDetailItem), add an af:setPropertyListener as shown below:



As can be seen above, on tab disclosure event for Administration tab, we are setting property #{pageFlowScope.disclosedTab} as “administration”. Similarly set properties for all other tabs.


  
  
 
  




  
  
 
  




  
  
 
  




  
  
 
  



Step 5.2 In page definition select individual task flow instances and set the activation properties as shown below:



Above is task flow instance for Administration tab. Since on initial page load this tab is disclosed by default, we want its task flow instance to get executed. This is why we are adding to the activation condition #{pageFlowScope.disclosedTab == null} check. For other tabs the condition will be as follows.









Step 6: You are now ready to run and test the page :)



Alternative approach

Another approach to execute a view object with different values of query bind variables will be to expose an AM method and implement the UI side bean code to call it. In our approach we are doing the same without writing a single line of code and utilizing the ADF out of box action.

Summary

Hope you enjoyed this little blog. Until next time, live long, prosper and… Happy Coding!

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.

Source Code: http://www.guardiansoftheoracleworld.com/blogs/adfman/2015/20150826_Using_execute_with_params.zip

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