Open ESB Tip : Developing the WLM SE Worklist Manager Interface using Visual Web Pack

The WLM SE provides a simple Web based Worklist Manager that allows users to review, Checkout and Complete Tasks assigned to them. This is a very basic interface and in unlikely, although it can be modified, to provide exactly what you want. Given that most users would like their own Company specific interface linking in with their portal and access control system they  may well need to write their own interface.

This blog will take you through implementing a Visual Web Pack interface to the WLM SE processes. By its nature I intend to make this a simplistic implementation but hopefully show how it can be done. In my next blog I will repeat the process using the IceFaces interface.

Following on from this implementation I will integrate the pages with Open SSO to provide the Authentication and Authorisation functionality.


WLM SE Trail
  1. Configuring WLM SE to use Open DS.
  2. Implementing a Vendor Relationship Management Portal (Part 3) (VRMP Build Video).
  3. Developing the Worklist Interface using Visual Web Pack.
  4. Developing the Worklist Interface with ICEfaces.
  5. Adding Security Using OpenSSO.


Resources

Application Overview


The Web Application defined below will provide a simple two page (excluding the login page) application that will display a list of User Assigned Tasks and allow the user to View (which will display the second page), Accept or reject a Task. Based on the response the application will call the appropriate TaskCommon operation. If the user decided to view the data then a second page will be displayed showing the actual message and a number of editable field where the user may change the values within the message before accepting the message.

WLMPOWebApp



  1. New Project -> Web -> Web Application



  2. Next
  3. New Web Application
    • Project Name : WLMPOWebApplication



  4. Next



  5. Next
  6. Frameworks Page
    • Select Visual Web JavaServer Faces



  7. Finish
We will now need to create a Web Service Client that is based on the TaskCommon.wsdl within the previously built WLMPOWorklistApp (see VRMP Part 3 blog entry). Therefore before proceeding make sure that you Clean & Build this project then create the client as follows:
  1. Right Click on the WLMPOWebApplication and select New->Web Service Client.



  2. Navigate to the build directory of the WLMPOWorklistApp and select TaskCommon.wsdl by selecting Browse for the Local File.




  3. Finish
This will create a Web Service Client that has the following Methods Available.



For initial testing I quickly created a Logon Page and simple page Header Fragment which I do not intend documenting. In addition to these I also created two additional Session Properties that will be used throughout the project.

// Session Properties

private String username = null;

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

private String currentPageName = "";

public String getCurrentPageName() {
    return currentPageName;
}

public void setCurrentPageName(String currentPageName) {
    this.currentPageName = currentPageName;
}

Before we can start accessing the WLM SE Task Database we will need to create a number of methods to access the appropriate Web Service Client calls. These methods can then be linked into the appropriate Visual Web Pack Data Providers. I will define these methods just prior to their use. The first method we will need is a getTaskList() which will be used to display the list of Tasks available for the use (as specified on the login page). Create the method in the SessionBean as follows:
  1. Open SessionBean.
  2. Create method public List<TaskType> getTaskList().
  3. Drag the GetTaskList operation from the Web Service Client and drop it between the {} of the Java Method. This will create an appropriate try-catch statement that calls the Web Service Operation.
  4. Modify the resultant code so it matches the following:
public List<TaskType> getTaskList() {
List<TaskType> taskList = new ArrayList<TaskType>();

try { // Call Web Service Operation
sun.com.jbi.wfse.wsdl.taskcommon.TaskCommonPortType port = service.getTaskCommonPort();
// TODO initialize WS operation arguments here
sun.com.jbi.wfse.wsdl.taskcommon.QueryType queryString = new sun.com.jbi.wfse.wsdl.taskcommon.QueryType();
java.lang.String userId = getUsername();
int start = 0;
int pageSize = 0;
// TODO process result here
sun.com.jbi.wfse.wsdl.taskcommon.TaskListType result = port.getTaskList(queryString, userId, start, pageSize);
taskList = result.getTask();
} catch (Exception ex) {
// TODO handle custom exceptions here
}

return taskList;
}
Clean and Build the project to generate the classes used in the next part, if this is not done then you may not see the taskList property in the SessionBean.

Now we can create the TaskList Page as follows:
  1. Refactor and rename the default Page1 to TaskListPage or create a new Page.
  2. Drag a Table component onto the page.
  3. Right Click the table component and select "Table Layout"
  4. Change the "Get Data From" to be the taskList Property in the SessionBean:



  5. Modify the Displayed Attributes as follows :



  6. Modify the Sort / Pagination Options as follows:



  7. This will create the following:



  8. Add the header fragment and test.



  9. Edit the Table Layout and add 3 extra columns (no title). These should be Button Types with the Text as follows:
    • View.
    • Reject.
    • Accept.
  10. For each of these new column types add an Action to the Buttons follows:

View Button

For the View Button we will simple access the Task Data and then display it on a new page. Because the data stored within the Task as an XML Node type I have copied the com.sun.workflow.xml.Util.java from the sample Web Application to allow me to format the XML within the Node. Once we have accessed the Task Input Data we will copy it into the SessionBean and then pass this the ViewTask Screen (Defined later). To facilitate this we need to create some additional properties in the SessionBean.

SessionBean
private String inputXML = "";

public String getInputXML() {
return inputXML;
}

public void setInputXML(String inputXML) {
this.inputXML = inputXML;
}

private Long taskId = new Long(0);

public Long getTaskId() {
return taskId;
}

public void setTaskId(Long taskId) {
this.taskId = taskId;
}

private String taskTitle = "";

public String getTaskTitle() {
return taskTitle;
}

public void setTaskTitle(String taskTitle) {
this.taskTitle = taskTitle;
}

private Long taskPriority = new Long(0);

public Long getTaskPriority() {
return taskPriority;
}

public void setTaskPriority(Long taskPriority) {
this.taskPriority = taskPriority;
}


View Button Action
/\*
\* Methods
\*/
private Node getTaskInput() {
Node taskInputNode = null;
// Get TaskId
Long taskIdLong = (Long) getValue("#{currentRow.value['taskId']}");
System.out.println("\*\*\* APH-I1 : getTaskInput() TaskId " + taskIdLong);
if (taskIdLong != null) {
try {
sun.com.jbi.wfse.wsdl.taskcommon.TaskCommonPortType port = service.getTaskCommonPort();
// TODO initialize WS operation arguments here
long taskId = taskIdLong.longValue();
java.lang.String userId = getSessionBean1().getUsername();
// TODO process result here
java.lang.Object result = port.getTaskInput(taskId, userId);

if (result != null && result instanceof Node) {
taskInputNode = (Node) result;
String xml = Util.toXml(taskInputNode, "UTF-8", true);
System.out.println("\*\*\* APH-I3 getTaskInput: " + xml);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

return taskInputNode;
}


/\*
\* Action Event Handlers
\*/
public String viewBtn_action() {

Node taskOutputNode = getTaskOutput();
getSessionBean1().setOutputNode(taskOutputNode);
getSessionBean1().setInputXML(Util.toXml(taskOutputNode, "UTF-8", true));
getSessionBean1().setTaskId((Long) getValue("#{currentRow.value['taskId']}"));
getSessionBean1().setTaskTitle((String) getValue("#{currentRow.value['title']}"));
getSessionBean1().setTaskPriority((Integer) getValue("#{currentRow.value['priority']}"));
return "view";
}
We will create the page navigation for the View Task Page later.

Reject Button

The Reject (and Accept) Button will combine a number of actions that will Claim the Task copy the Task Output to the modify the ApprovalResult to either Rejected or Approved (because we are not really doing anything) and then complete the task. This is achieved by creating the completeTask methods dragging methods from the Web Service client into it. The Reject Button action will call the completeTask passing false to the approve boolean.

This should then be modified to match the following:

public String rejectBtn_action() {
completeTask(false);
return null;
}

private void completeTask(boolean approve) {
Node outputMsgNode = null;
// Get TaskId
Long taskIdLong = (Long) getValue("#{currentRow.value['taskId']}");
System.out.println("\*\*\* APH-I1 : TaskId " + taskIdLong);
if (taskIdLong != null) {
try { // Call Web Service Operation
sun.com.jbi.wfse.wsdl.taskcommon.TaskCommonPortType port = service.getTaskCommonPort();
// TODO initialize WS operation arguments here
long taskId = taskIdLong.longValue();
java.lang.String userId = getSessionBean1().getUsername();
// TODO process result here
sun.com.jbi.wfse.wsdl.taskcommon.ResultCodeType result = port.claimTask(taskId, userId);
java.lang.Object outputMsg = port.getTaskOutput(taskId, userId);
if (outputMsg != null && outputMsg instanceof Node) {
outputMsgNode = (Node) outputMsg;
String xml = Util.toXml(outputMsgNode, "UTF-8", true);
System.out.println("\*\*\* APH-I3 getTaskOutput: " + xml);
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
Node newChildNode = null;
boolean approvalResultFound = false;
for (int i=0;i<childNodeList.getLength();i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("ApprovalResult")) {
approvalResultFound = true;
newChildNode = oldChildNode.cloneNode(true);
if (approve)newChildNode.setNodeValue("Approved");
else newChildNode.setTextContent("Rejected");
outputMsgNode.replaceChild(newChildNode, oldChildNode);
i = childNodeList.getLength();
}
}
outputMsg = outputMsgNode;
xml = Util.toXml(outputMsgNode, "UTF-8", true);
System.out.println("\*\*\* APH-I3 getTaskOutput: " + xml);
} else {
warn("outputMsg is not a Node");
}
result = port.setTaskOutput(taskId, outputMsg, userId);
result = port.completeTask(taskId, userId);
} catch (Exception ex) {
ex.printStackTrace();
error(ex.getMessage());
}
}
}

Accept Button

The Accept Button action will call the completeTask passing false to the approve boolean.

public String acceptBtn_action() {
completeTask(true);
return null;
}

We can now create the ViewTaskPage and link it to the appropriate SessionBean properties to display the data correctly. Create a new Page and add the Buttons, Grid Panels and fields so that it looks like the following:



Within the SessionBean we will create the following Methods that allow access to the Node information:

/\*
\* Node Processing methods
\*/
private Node outputNode = null;

public Node getOutputNode() {
return outputNode;
}

public void setOutputNode(Node outputNode) {
this.outputNode = outputNode;
}
private String poNumber = "";
private String supplier = "";
private String description = "";
private boolean approve = false;

public boolean isApprove() {
Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("ApprovalResult")) {
if (oldChildNode.getTextContent().equals("Approved")) {
approve = true;
} else {
approve = false;
}
System.out.println("\*\*\* APH-I1 : Approved " + approve + " (" + oldChildNode.getTextContent() + ")");
i = childNodeList.getLength();
}
}
}
return approve;
}

public void setApprove(boolean approve) {
this.approve = approve;

Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
Node newChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("ApprovalResult")) {
newChildNode = oldChildNode.cloneNode(true);
if (approve) {
newChildNode.setTextContent("Approved");
} else {
newChildNode.setTextContent("Rejected");
}
System.out.println("\*\*\* APH-I1 : Approved " + approve + " (" + oldChildNode.getTextContent() + ")");
outputMsgNode.replaceChild(newChildNode, oldChildNode);
i = childNodeList.getLength();
}
}
}
}

public String getDescription() {
Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("PODescription")) {
description = oldChildNode.getTextContent();
i = childNodeList.getLength();
}
}
}
return description;
}

public void setDescription(String description) {
this.description = description;

Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
Node newChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("PODescription")) {
newChildNode = oldChildNode.cloneNode(true);
newChildNode.setTextContent(description);
outputMsgNode.replaceChild(newChildNode, oldChildNode);
i = childNodeList.getLength();
}
}
}
}

public String getPoNumber() {
Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("PONumber")) {
poNumber = oldChildNode.getTextContent();
i = childNodeList.getLength();
}
}
}
return poNumber;
}

public void setPoNumber(String poNumber) {
this.poNumber = poNumber;

Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
Node newChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("PONumber")) {
newChildNode = oldChildNode.cloneNode(true);
newChildNode.setTextContent(poNumber);
outputMsgNode.replaceChild(newChildNode, oldChildNode);
i = childNodeList.getLength();
}
}
}
}

public String getSupplier() {
Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("SupplierName")) {
supplier = oldChildNode.getTextContent();
i = childNodeList.getLength();
}
}
}
return supplier;
}

public void setSupplier(String supplier) {
this.supplier = supplier;

Node outputMsgNode = getOutputNode();
if (outputMsgNode != null) {
// Iterate through child nodes
NodeList childNodeList = outputMsgNode.getChildNodes();
Node oldChildNode = null;
Node newChildNode = null;
for (int i = 0; i < childNodeList.getLength(); i++) {
oldChildNode = childNodeList.item(i);
if (oldChildNode.getNodeName().endsWith("SupplierName")) {
newChildNode = oldChildNode.cloneNode(true);
newChildNode.setTextContent(supplier);
outputMsgNode.replaceChild(newChildNode, oldChildNode);
i = childNodeList.getLength();
}
}
}
}
To display the data correctly we will need to bind the fields on the page as follows:
  • Task Id Static Text : #{SessionBean1.taskId}
  • Title Static Text : #{SessionBean1.taskTitle}
  • Priority Static Text : #{SessionBean1.taskPriority}
  • Message Text Area : #{SessionBean1.inputXML}
  • tfPONumber : #{SessionBean1.poNumber}
  • tfSupplierName : #{SessionBean1.supplier}
  • tfDescription : #{SessionBean1.description}
In addition to this we will add the following Code to the Button Actions:

private void completeTask(boolean approve) {
// Get TaskId
Long taskIdLong = getSessionBean1().getTaskId();
System.out.println("\*\*\* APH-I1 : TaskId " + taskIdLong);
if (taskIdLong != null) {
try { // Call Web Service Operation
sun.com.jbi.wfse.wsdl.taskcommon.TaskCommonPortType port = service.getTaskCommonPort();
// TODO initialize WS operation arguments here
long taskId = taskIdLong.longValue();
java.lang.String userId = getSessionBean1().getUsername();
// TODO process result here
sun.com.jbi.wfse.wsdl.taskcommon.ResultCodeType result = port.claimTask(taskId, userId);
getSessionBean1().setApprove(approve);
result = port.setTaskOutput(taskId, getSessionBean1().getOutputNode(), userId);
result = port.completeTask(taskId, userId);
} catch (Exception ex) {
ex.printStackTrace();
error(ex.getMessage());
}
}
}

public String listBtn_action() {
// TODO: Process the action. Return value is a navigation
// case name where null will return to the same page.
return "list";
}

public String rejectBtn_action() {
completeTask(false);
return "reject";
}

public String acceptBtn_action() {
completeTask(true);
return "accept";
}

Finally we need to set the Page Navigation (Right Click on the page and select Page Navigation) up as follows.



Clean and Build the project and resolve any errors.

WLMPOWebCompositeApp



  1. New Project -> SOA -> Composite Application



  2. Next
  3. New Composite Application Project
    1. Project Name : WLMPOWebCompositeApp



  4. Finsh
  5. Add JBI Module WLMPOWebApplication.war



  6. Clean & Build
  7. Open Service Assembly



    1. Add External Service Unit
      1. Name : WLMSE-TASKCommon



    2. Drag Provider EndPoint onto External



    3. Select EndPoint Properties and edit as follows:
      • Interface Name : Click ellipsis TaskCommonPortType



      • Service Name : Click ellipsis TaskCommonService



      • Endpoint Name : TaskCommonPort



    4. Connect the javaee_TaskCommonPort in the JBI Module to the TaskCommonPort in the External.



    5. Select the Provide on the existing WSDL Port SOAP interface and then check the "Disable in BC" property.



    6. Clean & Build
    7. Deploy

Running The Web Application


Now that the build process has been completed we can run the Web Application. I am assuming that the Business Process has been loaded with the appropriate Workflows. Once this has been done the application will look as follows:

Login

Login

Task List

Task List

Task View
Select View from the Task List.

Task View

Revised Task List
Select Accept from the Task View,

Task List


You can try the various combinations of buttons. You will notice that both Accept and Reject mark the Task Status as completed because it has but when Reject is selected the BPEL process is terminated because we map Rejected into the XML Element.

If you used the previous set of WorkList Task you will need to remove the code for the Completed Action because we now do that mapping in the Web Pages.

Comments:

Can you explain to me why I need to wrap the Web Application in a Composite Application?
I do not understand why the TaskCommon functionality is not exposed directly for anyone to use, or maybe it is but i don't see where.

If i were to build a WLM Worklist Manager interface using for example django, is there no way for me to communicate with the WLM "API"
The only solution i can think of is to create a web-service layer on top of the Web Applications TaskCommon client.

Posted by Bjorn on August 18, 2010 at 10:02 AM GMT #

Bjorn,

In earlier releases (Beta) you could in fact separate the functionality but for the later releases the code line was changed to require direct linking within the CA. This was partly due to performance but also simplified some of the internal code.

Personally I have raised the request to separate the code but to allow what you want and it was noted as an enhancement.

Posted by Andrew on August 24, 2010 at 01:03 AM GMT #

Post a Comment:
Comments are closed for this entry.
About

As a member of the Oracle A-Team we specialise in enabling and supporting the Oracle Fusion Middleware communities.

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today