Implementing Task Initiation

This post is part of a series on building a custom worklist for BPM/SOA 11g.

In the post we will implement two Controllers and a View that will allow the use to view a list of tasks that they are able to initiate, and to actually initiate a task.  These Controllers will be a little different the the ones we have been looking at so far.  Firstly, they use some APIs from a different package, of course this is hidden in the MTaskList class, so we wont see that, but we have the added complication of needing to open the Task Form for the user.

When the user initiates a task, our MTaskList.initiateTask() method will create an instance of the relevant process and obtain a special URL that allows us to access the Task Form.  We need to pass this URL back through the Controller and put it into the Model so that we can popup a new window with the Task Form in it.  In order to make this work, we will see one or two additional techniques being used in these Controllers that we have not seen before.

Getting a list of tasks that can be initiated

Here is the code for com.oracle.ateam.InitiateListController, which is responsible for getting a list of the tasks that can be initiated by the logged in user:

package com.oracle.ateam;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oracle.bpm.services.processmetadata.ProcessMetadataSummary;
import java.util.HashMap;
import java.util.Map;import java.util.List;
import com.oracle.ateam.domain.MTaskList;
import com.oracle.ateam.util.MLog;

public class InitiateListController extends SimpleSuccessFailureController {
  private String xTaskformUrl = null;

  @Override  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    MLog.log("InitiateListController", "Entering handleRequest()");
    Map<String, Object> model = new HashMap<String, Object>();
    List<ProcessMetadataSummary> itasks = MTaskList.getInitiateLinks(request.getRemoteUser());
    model.put("itasks", itasks);

    // check if we need to pop a task form
    if (xTaskformUrl != null) {
      // we need to pop a form
      MLog.log("InitiateListController", "Need to pop a form");
      model.put("x_taskform_url", xTaskformUrl);
    }
    return new ModelAndView("/WEB-INF/jsp/initiatelist.jsp", "model", model);
  }

  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, String url)
      throws Exception {
    xTaskformUrl = url;
    return handleRequest(request, response);
  }
}

If we look at the ModelAndView handleRequest(HttpServletRequest, HttpServletReponse) method, i.e. the ‘normal’ one that we are used to seeing, the first on in the listing above, we can see that it does more or less follow the same pattern we are used to.  It does not need to get any data from the caller, because all we need for this API is to know which user is logged in, and we can get that from the HttpServletRequest.  It calls the getInitiateLinks() method on MTaskList and puts the resulting List<ProcessMetadataSummary> into the model as “itasks”.

Now we do something slightly different.  In order to let the view know about the Task Form URL, we need to introduce some local state, xTaskformUrl.  We will see shortly how this is populated when the user initiates a task.  In this Controller, we check to see if it has a URL in it, and if it does, we also put that into the model as “x_taskform_url”.

There is also a second handleRequest() method here with a slightly different signature which includes a String url.  We will see shortly how this is used.  But for now, just note that it sets the xTaskformUrl to the url value passed in, and then calls the other (normal) handleRequest method that we just looked at.

Let’s take a look at the View to display the list of initiatable tasks to the user.  Here is the code from src/main/webapp/WEB-INF/jsp/initiatelist.jsp:

<%@ page import="com.oracle.ateam.util.MLog" %>
<%@ page import="oracle.bpm.services.processmetadata.ProcessMetadataSummary" %>
<%@ page import="java.util.List" %>
<%@ include file="/WEB-INF/jsp/common/head.jspf" %>
<%
 MLog.log("initiatelist.jsp", "Entered initiatelist.jsp");
 // check if we need to pop a form
%>
  <c:choose>
    <c:when test="${model.x_taskform_url eq null}">
    </c:when>
    <c:otherwise>
      <script language="JavaScript">
        <!--
        window.open ("${model.x_taskform_url}", "TaskForm", "menubar=0,resizable=1,width=640,height=640");
        -->
      </script>
    </c:otherwise>
  </c:choose>
  <p>Select a task to initiate:</p>
  <ul>
    <c:forEach var="itask" items="${model.itasks}">
      <li>
        <a href="initiatetask.do?x_composite_dn=${itask.compositeDN}/${itask.processName}">
        [${itask.compositeName}] ${itask.processName} v${itask.revision}
        </a>
      </li>
    </c:forEach>
  </ul>
<%@ include file="/WEB-INF/jsp/common/tail.jspf" %>
<%  MLog.log("initiatelist.jsp", "Done");%>

This is a relatively simple page, however we do see a new technique here that we have not used yet.  The first part of the page checks whether we need to pop up a Task Form.  This will only happen after the page has been displayed and the user has clicked on one of the links to start a new task, which as we will see in a moment, would invoke the InitiateTaskController which in turn would invoke InitiateListController and then forward back to this same View.

In the first part of the page we see a JSTL choose/when/otherwise construct that checks if a Task Form URL has been provided using the following Expression Language expression:

${model.x_taskform_url eq null}

If there is one present, we inject some JavaScript into the page to open a popup window using the URL:

window.open ("${model.x_taskform_url}", "TaskForm", "menubar=0,resizable=1,width=640,height=640");

In the second part of the page, we return to our familiar pattern of iterating over the list of tasks in the model and displaying some information about them.  Note that we make each line a link to pass control to the InitiateTaskController and pass in the compositeDN, which is used to identify which task to initiate.

    <c:forEach var="itask" items="${model.itasks}">
      <li>
        <a href="initiatetask.do?x_composite_dn=${itask.compositeDN}/${itask.processName}">
        [${itask.compositeName}] ${itask.processName} v${itask.revision}
        </a>
      </li>
    </c:forEach>

We are displaying the tasks in the format [Composite Name] Process Name vRevision, the same as the ‘out of the box’ workspace application does.  Of course, you could display your tasks in any way you like.  A future enhancement might be to provide a mechanism to provide a mapping from these names to more user-friendly names for the tasks.

Let’s now move on and take a look at what happens when the user clicks on a task, and see how all these pieces fit together 🙂

Initiating a task

Here is the com.oracle.ateam.InitiateTaskController which will actually initiate a task that the user has selected from the list.

package com.oracle.ateam;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oracle.bpm.services.processmetadata.ProcessMetadataSummary;
import java.util.HashMap;
import java.util.Map;import java.util.List;
import com.oracle.ateam.domain.MTaskList;
import com.oracle.ateam.util.MLog;

public class InitiateTaskController extends SimpleSuccessFailureController {

  @Override
  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    MLog.log("InitiateTaskController", "Entering handleRequest()");
    String compositeDN = request.getParameter("x_composite_dn");
    String url = MTaskList.initiateTask(request.getRemoteUser(), compositeDN);
    MLog.log("InitiateTaskController", "Got url: " + url);
    return new InitiateListController().handleRequest(request, response, url);
  }
}

This Controller is very similar to the others we have seen, it retrieves x_composite_dn from the HttpServletRequest and invokes the initiateTask() method on our MTaskList object.  Note though that this method returns a String url which we need to pass to the next chained Controller, the InitiateListController that we just looked at, so that it can pass it on to the View which will display the Task Form for the user.

Note that we use the second/alternate handleRequest() method to invoke the chained InitiateListController.

When the InitiateListController runs this time, it will find the URL is present and will pass it through to the View, which will find it and launch the Task Form in a popup window.

Important Note

In order for this to work ‘properly,’ you need to make sure you generate the Empty1 page in the adfc-config unbounded task flow in your Human Task applications.  JDeveloper does not do this by default, so you will need to open the Task Flow and double click on that view to create a jspx page.  If you do not take this extra step, you will get a ‘404’ error in your pop up Task Form window after you hit the Submit button on the task.

Summary

Wow!  We have completed our ‘Version 1.0’ custom worklist, now let’s move on to the next post where we will deploy it and test it out!

About Mark Nelson

Mark Nelson is an Architect (an "IC6") in the Fusion Middleware Central Development Team at Oracle. Mark's job is to make Fusion Middleware easy to use in the cloud and at home, for developers and operations folks, with special focus on continuous delivery, configuration management and provisioning - making it simple to manage the configuration of complex environments and applications built with Oracle Database, Fusion Middleware and Fusion Applications, on-premise and in the cloud. Before joining this team, Mark was a senior member of the A-Team since 2010, and worked in Sales Consulting at Oracle since 2006 and various roles at IBM since 1994.
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s