2012. november 12., hétfő

Problems with update() Operation in Human Task Data Controls

Suppose you have an ADF UI implementing a BPM human task, i.e. a bounded task flow using the generated human task data controls (based on the task definition), and your specific BTF implementation with your pages. To update attributes in the BPM payload, you can use the update() operation of the HT data control. This is working fine on the auto-generated task form and on any other JSF page.

In ADF task flows, any kind of activity can have bindings/pageDefs defined, which means that method call or data control operation activities in your BTF can be used to access your custom BPM payload or any other BPM metadata coming from the getTaskDetails() operation in the data control.

The sad part comes as soon as you try to call the update() operation directly from the BTF, and not from a JSF view (e.g. in a button action). For example, you can do that in a method call activity which has an associated binding container that contains all the necessary bindings for getTaskDetails(), its results, and the update() operation inside it. But then you realize that it is not working, and throws the following NullPointerException:

<Apr 11, 2012 11:41:48 AM CEST> <Error> <oracle.adf.model.adapter>
<BEA-000000> <Exception invoking method from XML data control.
       at oracle.bpel.services.workflow.datacontrol.WorkflowService.saveHistory(WorkflowService.java:831)
       at oracle.bpel.services.workflow.datacontrol.WorkflowService.invokeWorkflowDCMethods(WorkflowService.java:236)
       at oracle.bpel.services.datacontrol.XSDDataControl.invokeWorkflowOperation(XSDDataControl.java:373)
       at oracle.bpel.services.datacontrol.XSDDataControl.invokeOperation(XSDDataControl.java:467)
       at oracle.adf.model.bean.DCBeanDataControl.invokeMethod(DCBeanDataControl.java:430)
       at oracle.adf.model.binding.DCInvokeMethod.callMethod(DCInvokeMethod.java:261)
       at oracle.jbo.uicli.binding.JUCtrlActionBinding.doIt(JUCtrlActionBinding.java:1635)
       at oracle.adf.model.binding.DCDataControl.invokeOperation(DCDataControl.java:2149)
       at oracle.adf.model.bean.DCBeanDataControl.invokeOperation(DCBeanDataControl.java:467)
       at oracle.adf.model.adapter.AdapterDCService.invokeOperation(AdapterDCService.java:307)
       at oracle.jbo.uicli.binding.JUCtrlActionBinding.invoke(JUCtrlActionBinding.java:740)
       at oracle.adf.controller.v2.lifecycle.PageLifecycleImpl.executeEvent(PageLifecycleImpl.java:402)
       at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding._execute(FacesCtrlActionBinding.java:252)
       at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding.execute(FacesCtrlActionBinding.java:210)
       at mypackage.MyBean.doSomethingAndUpdate(MyBean.java:666)

After hours of investigation and trial and error of no avail, and without having the sources of the XSDDataControl and WorkflowService classes (which can be found in bpm-workflow-datacontrol.jar), one can only guess why this is happening. (Which, to be honest, is mostly the case when developing human task interfaces, thanks to the poor documentation on human task form development.)

Again, it works when called from a view, but fails with a NPE when called from a method call activity in an ADF task flow, i.e. outside a view, where you do have a FacesContext, but not a UIViewRoot as you have not yet entered any view activity. Is it possible that the WorkflowService.saveHistory() method tries to access the JSF view root? Let's give it a try, and create a fake view before calling update():

// in the method call activity
String someFakeViewId = ...;
FacesContext fctx = FacesContext.getCurrentInstance();
Application application = fctx.getApplication();
ViewHandler viewHandler = application.getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(fctx, someFakeViewId);
// update() after this

If you try this, you can see that it's working now. So we can (vaguely) guess that some call is being made on FacesContext.getViewRoot() in WorkflowService.saveHistory(), which of course does not makes much sense (i.e. calling back to the view layer from a data control).

Until this gets fixed, you can use this as a workaround. Or just avoid using method call activities for BPM payload updates... ;)

Nincsenek megjegyzések:

Megjegyzés küldése