January 4 2012

Oracle BPM 11g (or BPEL)  uses XML to pass along process payload information.  To modify this information in a UI, BPM data controls are typically used to update this data from the ADF user interface. This is a well known technique and easy to do.  These data controls can be generated from the human task UI wizard or through the new gallery's "ADF Task Flow Based on Human Task", Figure 1.  So long as the UI uses the BPM's data controls, all is well.

Figure 1. Creating Task Flow from BPM Human Task

But using the BPM data controls (Figure 2) isn't always what we need, is it?  Many times the UI content doesn't originate from BPM; only small portions of the data reside in the BPM payload.  In many cases it comes from a data base, POJOs, or web services.  Here the UI forms are not tied to BPM data, but something else.  When this happens we may still want to update the BPM payload, even though the UI forms and data controls update some other back-end system and when this happens we need a way to update the BPM payload.  It is a little tricky, but not diffcult, if you know how.

Figure 2. BPM Data Controls

To implement a UI not tied to the BPM data controls, there are two things you need to do.  The first is to write some Java code to get and update BPM's payload data. This is done using a managed bean called Task tied to the task-flow.  The code you need is contained below, but this is the easy part.  The tricky part, and second thing we need to do, is to intercept the call to the BPM submit operations, like Accept or Reject.  We need to intercept this call so changes can be made to the BPM payload before control is passed back to the BPM process.  If you try it in the task-flow after someone clicks on one of the BPM linked <af:commandToolbarButton>, it will be too late.  So I will show you how to intercept this call at the right time. 

But before I show you this tricky part, let me give you some code you'll need to get the BPM Task managed bean and and show you how to modify the payload information. First the import statements: 

import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.datacontrol.WorkflowService;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

And now the code.  This code is in a method called modifyBPMPayloadData. It gets the active Task object from the Java Faces task-flow context:       

void modifyBPMPayloadData() {

FacesContext context = FacesContext.getCurrentInstance();
String tskId =  (String)context.getApplication().evaluateExpressionGet(context,
                                                    "#{pageFlowScope.bpmWorklistTaskId}",  String.class);

IWorkflowServiceClient workflowSvcClient =   WorkflowService.getWorkflowServiceClient();
ITaskService taskSvc = 
workflowSvcClient .getTaskService();
ITaskQueryService wfQueryService =  workflowSvcClient.getTaskQueryService();
IWorkflowContext wfContext =   WorklistUtil.getWorkflowContextForASelectedTask();
Task myTask = wfQueryService.getTaskDetailsById(wfContext, tskId);

Element payloadElem = myTask.getPayloadAsElement()

/*
Put your code in here to modify BPM payload data
*/

myTask.setPayloadAsElement(payloadElem);
taskSvc .updateTask(wfContext ,  task);

}

Let's break down this Java method.  The code up to "wfQueryService.getTaskDetailsById" is needed to locate the instance of the Task object and the BPM payload data.  Once that is done, there are two useful methods on the Task Class;

Element payloadElem = myTask.getPayloadAsElement()
myTask.setPayloadAsElement(payloadElem);

You can use these methods to get the BPM payload data, modify it, and place it back into the Task instance.  After you set the payload back on the Task, all you have to do is notify the workflow context that the data has changed, as shown here:

workflowSvcClient.updateTask(wfContext ,  task);

Okay, now you should be wondering, when is this done?  And this is the tricky part I told you about.  This has to be done before control is passed back to the BPM process.  And in ADF BPM data controls, control is passed back to BPM when one of the human task action buttons are selected.  If you look for these action buttons, you see some XML statements like this one in your jspx file:

<af:commandToolbarButton

            actionListener="#{invokeActionBean.setOperation}"

            text="#{wf:getResourceValue('REJECT', 'bindings.customActions')}"

            disabled="#{!bindings.REJECT.enabled}"

            action="#{invokeActionBean.invokeOperation}"

            partialSubmit="false"

             id="ctb10">

    <f:attribute name="DC_OPERATION_BINDING”  value="bindings.REJECT"/>

</af:commandToolbarButton>

When using BPM-based data controls, these action buttons, like "Reject",  simply pass control back to your process through a web service call-back operation.  This updates the BPM payload and tells the BPM process what action was taken; e.g. "Reject".  We can intercept this call by modifying the action listener to one of our own.  This action listener will call the code that modifies the BPM payload before control is passed back to your business process. Simply modify the actionListener method to something, like this:

<af:commandToolbarButton

           actionListener="#{myClientServiceHelper.mySetOperation}"

           text="Reject"

           disabled="#{!bindings.REJECT.enabled}"

            action="#{invokeActionBean.invokeOperation}"

            partialSubmit="false"

            id="ctb2" rendered="false">

             <f:attribute name="DC_OPERATION_BINDING”  value="bindings.REJECT"/>

 </af:commandToolbarButton>

In  this exemple, myClientServiceHelper is a java bean registered with the task flow and contains a method like this one:

 public void mySetOperation(ActionEvent action) {

      FacesContext fctx = FacesContext.getCurrentInstance();
      Application app = fctx.getApplication();
      ELContext elContext = FacesContext.getCurrentInstance().getELContext();
      InvokeActionBean invokeActionBean = app.getELResolver().getValue(elContext, null,  " invokeActionBean");

       invokeActionBean.setOperation(action);

       modifyBPMPayloadData() ;  /* This is where you modify the BPM payload data using the code described above */

    }

 

This code acts like a proxy for the actionListener in the original call namely:

 

 actionListener="#{invokeActionBean.setOperation}"

 The mySetOperation action listener method makes this call after it locates theinvokeActionBean on the Faces Context.  Incidentally the InvokeActionBean class import statement is a follows:

       import oracle.bpel.services.workflow.worklist.adf.InvokeActionBean;

Once the proxy call is complete, this actionListener method calls the modifyBPMPayloadData() method described above.  This modifies and then updates the BPM payload before the BPM call back opertation is performed and control is passed back to the BPM process.

And that's all there is. Like I said it's a bit tricky, but not difficult if you know how.

 

I hope you found this helpful.  I used this code to update BPM payload data from an XML document generated by a complex set of JAXB classes. The JAXB classes were used as data controls in a UI through a POJO facade layer.  The JAXB data was marshalled to and from XML for use in a back-end web service interface.  Let me know if you have any questions about this blog or wish to learn more about using POJOs and JAXB classes in ADF Data Controls.  I may blog about this next. I hope to hear from you soon.

About the Author

Brandon Dean

Brandon Dean is Executive Vice President for AVIO and focuses his time on building client relationships and directing sales, marketing, and strategy initiatives for AVIO. Prior to joining AVIO in 2008, Brandon spent time in various positions at Oracle, BEA Systems, and Fuego where he built a reputation as a thought leader in BPM strategy and implementation advisory services. 

Join the Conversation

October 9, 2014

I was refering to your blog to update my task payload using database fields. I have put my database fields on the ADF form and on the submit button i want the data entered in five of the database fields to be copied to the corresponding fields in the payload. The actual Part to do that has not been included in this blog and being new to adf I am not aware of how to do that. So could you provide with any hints to do that.
Thanks

October 19, 2014

Thx for your effort.

i had error  after execution "The task is already modified. The task was modified before the current action could be performed. Refresh the task and perform the action again if the action is still relevant on the refreshed task."

on console 

<Oct 19, 2014 10:23:42 PM EET> <Warning> <oracle.adf.controller.faces.lifecycle.Utils> <BEA-000000> <ADF: Adding the following JSF error message: The task is already modified.
The task was modified before the current action could be performed.
Refresh the task and perform the action again if the action is still relevant on the refreshed task.

oracle.jbo.JboException: The task is already modified.
The task was modified before the current action could be performed.
Refresh the task and perform the action again if the action is still relevant on the refreshed task.

 

i dont know if this bug on version 1.70 or we have someing wrong in our sample .

Enter your first name. It will only be used to display with your comment.
Enter your email. This will be used to validate you as a real user but will NOT be displayed with the comment.
By submitting this form, you accept the Mollom privacy policy.