Enterprise Java

ADF: URL Task Flow Call with HTTP POST Method

As we know a bounded task flow can be invoked by some URL either directly from a browser or from some external application. This feature is enabled if task flow’s property “URL invoke” is set to “url-invoke-allowed” and it is commonly used in integration projects. Usually clients (or invokers) use HTTP GET method and pass their parameters in the URL. Let’s consider some simple task flow with one required input parameter:
 
 
 
 
 

  <task-flow-definition id="task-flow-definition">    
    <input-parameter-definition id="__23">
      <name id="__24">userName</name>
      <value id="__67">#{requestScope.userName}</value>
      <class id="__63">java.lang.String</class>
      <required/>
    </input-parameter-definition>    
    ...

The task flow can be invoked by the URL like this

http://127.0.0.1:7101/TestApp/faces/adf.task-flow?adf.tfId=task-flow-definition&adf.tfDoc=/WEB-INF/task-flow-definition.xml&userName=xammer

The client uses a simple html form to construct this GET request:

<html>
  <head>    
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  </head>
  <body>
   <form action="http://127.0.0.1:7101/TestApp/faces/adf.task-flow">
   <input type="hidden" name="adf.tfId" value="task-flow-definition"/>  
   <input type="hidden" name="adf.tfDoc" value="/WEB-INF/task-flow-definition.xml"/>  
   <label>     
        User Name 
      <input type="text" name="userName" value="xammer"/>  
   </label>
      <input type="submit" value="Submit"/>
    </form>
    </body>
</html>

And it looks like this:

Screen Shot 2013-08-02 at 5.55.16 PM

Some clients prefer to use HTTP POST method, and moreover this is their requirement:

<html>
  <head>    
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  </head>
  <body>
   <form action="http://127.0.0.1:7101/TestApp/faces/adf.task-flow" method="POST">
   <input type="hidden" name="adf.tfId" value="task-flow-definition"/>  
   <input type="hidden" name="adf.tfDoc" value="/WEB-INF/task-flow-definition.xml"/>  
   <label>     
        User Name 
      <input type="text" name="userName" value="xammer"/>  
   </label>
      <input type="submit" value="Submit"/>
   </form>
   </body>
</html>

And it works fine as well. The URL in this case is going to look like this:

http://127.0.0.1:7101/TestApp/faces/adf.task-flow

All other necessary information like task flow id and parameter value is inside POST request. But the problem is that it works fine for R1 only. If we try it out on R2 we will get the following:

ADF_FACES-30179:For more information, please see the server’s error log for an entry beginning with: The UIViewRoot is null. Fatal exception during PhaseId: RESTORE_VIEW 1.

Why? Because of that:

oracle.adfinternal.controller.application.InvokeTaskFlowException: ADFC-02006: A task flow ID is not found in the URL.
    at oracle.adfinternal.controller.util.UrlParams.getTaskFlowInfo(UrlParams.java:144)
    at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler.
invokeTaskFlowByUrl(RemoteTaskFlowCallRequestHandler.java:84)
    at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler.
doCreateView(RemoteTaskFlowCallRequestHandler.java:63)

All necessary data included task flow id which is supposed to be passed inside POST request is lost. Why?  Because of “loopback”. If we discover requests sent from the browser to the server on clicking the Submit button we’ll see the following:

Screen Shot 2013-08-02 at 6.41.27 PM

Screen Shot 2013-08-02 at 6.45.56 PM

So, instead of sending the “honest” response, the server sends some “loopback” script which generates “window id” and sends the following GET request with generated window id. Cool! But all post data is gone. The GET request is absolutely empty.

Fortunately, the framework doesn’t generate any “loopbacks” if the initial POST request has already some “window id”. So, the workaround for our case is to develop a servlet filter, setting the “window id” attribute for our request:

public void doFilter(ServletRequest servletRequest,
                     ServletResponse servletResponse,
                     FilterChain filterChain)
  throws IOException, ServletException
{
  HttpServletRequest r = (HttpServletRequest) servletRequest;
  HttpSession s = r.getSession();

  //May be this is not an initial request and window id has been generated earlier
  //We want all the following requests to work with the same window id 
  //For our use-case this is ok    
  String windowID = (String) s.getAttribute(_WINDOW_ID_KEY);
  if (windowID == null)
  {
    String pathInfo = r.getPathInfo();
    //This is an initial POST request to get access to the task flow
    if (("/adf.task-flow").equals(pathInfo) &&
        "POST".equals(r.getMethod()))
    {
      windowID = WINDOW_ID;
      //Save window id in the session 
      s.setAttribute(_WINDOW_ID_KEY, windowID);
    }

  }

  //Setup attribute for the request
  //This will prevent generating of the loopback
  if (windowID != null)
    r.setAttribute(_WINDOW_ID_KEY, windowID);

  filterChain.doFilter(servletRequest, servletResponse);
}

private static final String __WINDOW_MANAGER_KEY = RichWindowManager.class.getName();
private static final String _WINDOW_ID_KEY = __WINDOW_MANAGER_KEY + "#WINDOW_ID";  
private static final String WINDOW_ID = "wextflow";

Notice, that this filter should stand before “trinidad” filter in the filter chain:

  <filter>
    <filter-name>ExtPostFilter</filter-name>
    <filter-class>com.cs.fusion.core.view.filter.ExtPostFilter</filter-class>
  </filter> 
  <filter>
    <filter-name>trinidad</filter-name>
    <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class>
  </filter>
  <filter>
    <filter-name>ServletADFFilter</filter-name>
    <filter-class>oracle.adf.share.http.ServletADFFilter</filter-class>
  </filter>

That’s it!
 

Reference: URL Task Flow Call with HTTP POST Method from our JCG partner Eugene Fedorenko at the ADF Practice blog.

Eugene Fedorenko

I am a Senior Architect at Flexagon focusing on ADF and many other things.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button