Overview
This series provides a simple and flexible alternative way of creating task forms for human task activities in Oracle SOA/BPM Suite 11g. The pattern utilizes the power of Java and ADF Bounded Task Flows to simplify manipulation of task payloads and promote task form reusability, flexibility, and simplicity. Part 1 discusses about the problems and the corresponding solutions to attain the objective of having a reusable task form that can simplify the manipulation of task payload. The nitty-gritty details of the implementation of the generic task form will be discussed in Part 2, and the convenience and capabilities of this pattern will be demonstrated in Part 3.Problem Description
JDeveloper provides a mechanism to easily generate task forms for human task activities but the generated artifacts are mostly XML based and undeniably complex. Manipulation of payloads are very challenging especially if your domain models follows an Object Oriented approach, wherein you need not only capture object IDs but also all the other relevant attributes. Incorporation of complex ui components and validation scheme is a nightmare. Task form reuse and maintainability is compromised due to the need to generate different task forms for every change in task payload elements.Figure 1: JDeveloper Generated XML Artifacts |
Technical Pattern Description
This document is about creating a BPM task form based on Java with the following features:- a dynamic region to handle different task payload elements;
- actions are dynamic with regards to the access rights and the applicable outcomes/operations of a specific task;
- task payloads can be transformed into POJOs for convenient presentation, manipulation, and validation; and
- can be launched from the standard bpm worklist application.
Figure 2: A Java-Based BPM Task Form |
A task form for BPM human task is generally compose of the following sections:
- Task Actions
- Task Details
- Task Contents (Payload)
- Task History
- Comments
- Attachments
You can reuse a single task flow application with multiple human tasks. To use this feature, all human tasks must have identical payload elements.I doubt this information because what if the assigness have differently configured access rights and applicable Outcomes? Anyone from Oracle?
In short, we need to create our own simple implementation of reusable task form based on Java using the Human Task Services API that retrieves an oracle.bpel.services.workflow.task.model.Task object.
The next problems are as follows:
- How do we pass the taskflowId that our dynamic region requires, since that is not provided in the remote taskflow-call made by the standardard worklist application?
- How do we retrieve the updated payload after it has been displayed in the dynamic region and manipulated by the user?
The solution to problem#1 is to create wrapper taskflows wherein we can provide the appropriate taskflowId. This wrapper taskflow also contains all the required task flow input parameter that is needed by the generic human task that contains the task form with a dynamic region. In this wrapper tasflow, we also inject the necessary payload handler that is required to answer problem#2.
The solution to problem#2 is the pass-by-reference mechanism of task-input-parameters. We can pass a handler that contains the initial payload to the dynamic region and later access the same handler to get the updated payload. The handler will be responsible for marshalling and unmarshalling of xml contents into Java domain models and vice-versa. The handler will also contain the map of privileges that the user has with regards to the payload. Since the marshalling and unmarshalling process varies for every type of payload elements then we need to make the handler as an abstract class with two abstract methods- marshal and unmarshal.
Figure 3: A Simple Wrapper Task Flow Containing the Generic Task Flow |
package soadev.bpm.view.helper; import java.util.HashMap; import java.util.List; import java.util.Map; import oracle.bpel.services.workflow.metadata.IPrivilege; import oracle.bpel.services.workflow.task.model.AnyType; import org.w3c.dom.Element; public abstract class AbstractPayloadHandler { protected AnyType payload; protected Map<String, IPrivilege> visibilityRules; protected Map<String, Object> payloadObjects; public Map<String, Object> getPayloadObjects() throws Exception { if(payload == null){ throw new IllegalStateException("Payload not set."); } if (payloadObjects == null) { List<Element> elements = payload.getContent(); payloadObjects = new HashMap<String, Object>(); for (Element element : elements) { Object obj = unmarshal(element); payloadObjects.put(element.getNodeName(), obj); } } return payloadObjects; } public Map<String, Element> getPayloadElements() throws Exception { Map<String, Element> payloadElements = new HashMap<String, Element>(); for (Map.Entry<String, Object> entry : getPayloadObjects().entrySet()) { if(entry.getKey() != null && entry.getValue() != null){ Element element = marshal(entry.getValue(), entry.getKey()); payloadElements.put(entry.getKey() , element); } } return payloadElements; } public abstract Object unmarshal(Element element)throws Exception; public abstract Element marshal(Object object, String elementName)throws Exception; public void setPayload(AnyType payload) { this.payload = payload; } public AnyType getPayload(){ return payload; } public void setVisibilityRules(Map<String, IPrivilege> privileges) { this.visibilityRules = privileges; } public Map<String, IPrivilege> getVisibilityRules() { if(visibilityRules == null){ throw new IllegalStateException("Visibility Rules not set."); } return visibilityRules; } }The payload and visibilityRules attributes needs to be set upon the initialization of the task form.
public class TaskDetailsForm { ... public void initPayload() throws Exception { AbstractPayloadHandler handler = (AbstractPayloadHandler)ADFUtils.getPageFlowScope().get("payloadHandler"); if (handler != null) { handler.setPayload(getTask().getPayload()); handler.setVisibilityRules(this.getTaskVisibilityRules()); } } ...And the getPayloadElement will be called inside an updatePayload() method in the TaskDetailsForm. The updatePayload() will be invoked just before any task action like Submit or Approve will be invoked.
public void updatePayload() throws Exception { AbstractPayloadHandler container = (AbstractPayloadHandler)ADFUtils.getPageFlowScope().get("payloadContainer"); if (container != null) { Map<String, Element> payloadElements = container.getPayloadElements(); Element payloadElement = getTask().getPayloadAsElement(); for (Map.Entry<String, Element> entry :payloadElements.entrySet()) { if(hasWriteAccess(entry.getKey())){ PayloadUtil.replaceOrAppendPayloadElementData(payloadElement, entry.getValue()); } } getTask().setPayloadAsElement(payloadElement); } }Below is a sample class that extends the AbstractPayloadHandler class. This class takes advantage of JaxbContext for the marshalling and unmarshalling of XML elements to domain objects.
package soadev.bpm.view.helper; import com.oracle.xmlns.bpm.bpmobject.hellotypes.helloobject.HelloObjectType; import com.oracle.xmlns.bpm.bpmobject.hellotypes.reviewobject.ReviewObjectType; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; public class HelloWorldPayloadHandler extends AbstractPayloadHandler { private JAXBContext jaxbContext; public HelloWorldPayloadHandler() { try { jaxbContext = JAXBContext.newInstance(HelloObjectType.class, ReviewObjectType.class); } catch (JAXBException e) { throw new RuntimeException(e); } } public Object unmarshal(Element element) throws Exception { Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); Object obj = unmarshaller.unmarshal(element); return obj; } public Element marshal(Object object, String elementName) throws Exception { Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document document = docBuilder.newDocument(); marshaller.marshal(object, document); Element element = document.getDocumentElement(); return element; } }The HelloObjectType and ReviewObjectType classes were created by copying the HelloObject.xsd and ReviewObject.xsd from the sample HelloWorld_OBE BPM application into the task form project and invoking the Generate Jaxb 2.0 Content Model wizard of JDeveloper.
Figure 4: Generate JAXB 2.0 Content Model Wizard |
- Sample JAXB Application Converting POJO to XML Element and Vice-versa
- POJO TO SDO Marshalling through JAXBHelperContext
- How to Simply Convert SDO to DOM Element and Vice-versa
Hi Rommel,
ReplyDeleteI dont have much idea of BPM but i am good in ADF,
so just want to know the best practicews in terms of my requirment,
we have 8-10 letters, now each letter is intiated and user will generate the letter, now that letter will point to human task form created in ADF just to show user and edit if anything he wants before sending that letter.
now i am facing a challenge, for all the letter objects if we kept the same payload. I created different .jsff pages(regions) for each letter and dynamically opening a particular letter (based on the parameter coming from BPM) in taskdetails1.jspx. now how can i return the taskflow call to BPM from the dynamic region.
also if you can suggest on BPM side if we should generate different payload for each letter. Or any better approach to do the same.
Hi Rommel,
ReplyDeleteexcuse me, in the updatePayload() where i can find the PayloadUtil Class?
regards
Erick
Hi eYiCOCKo,
ReplyDeletePlease see the sample file on my second post: http://soadev.blogspot.com/2012/01/java-based-bpm-task-form-2.html
regards,
ROmmel
Hi Rommel,
ReplyDeletei understood maximum of the code ,
please send me steps how to add Oracle BPM libray into ADF project and how to deploy that.
i am getting error in deploying the application
like:Failed to register library Extension-Name: wlfullclient, Implementation-Version: 10.3.5.0: Library cannot have Implementation-Version set, without also specifying its Specification-Version.
Regards,
Shailendra
Hi Rommel,
ReplyDeleteHow we can implement the Dynamic Assignment..
My req. is like i have a supervisor where he receives 30 request . He need to reassign the task to his 3 Team leads 10 request for each Team lead. And this has to be done dynamically if his skills matches.
Regards,
Pavan