ADF Runtime Interface Generator for BPM Human Tasks

Version 7

    By Pedro Hugo do Nascimento Gabriel, Diogo Franqueira Henriques, and Danilo Alexandre Manmohanlal

     

    While developing several user interfaces for Human Tasks in Oracle Business Process Management (BPM) projects, we realized the necessity of creating a mechanism that would allow us to automate the process and reduce development time.

     

    The visual and declarative experience of ADF allows us to create these Human Tasks in two ways: by using the out-of-the-box options to auto-generate the Human Task form, or by manually creating UI pages as each Human Task form is designed. The first approach generates as many separate projects as there are Human Tasks; the latter creates as many user interface pages as there are Human Task forms.

     

    Since the majority of our interfaces for Human Tasks are similar, our approach to this issue was to develop an ADF taskflow library that contains a taskflow that we can drag and drop to our pages and render the Human Task based on input parameters.

     

    We also implemented a Java library that creates an abstraction layer of the BPM interactions. To this library we added some processing of the Human Task payloads, and provide basic caching of services and user contexts. We have found that having a single interface can decrease the development time of new processes by at least 85.7%. This article demonstrates how we implemented our Human Task interface generator for major customers in the Retail, Finance and Energy sectors.

     

    Introduction

     

    The projects that we have developed in Oracle BPM have plenty of interaction because of the use of Human Tasks; the options we have for development can involve massive complexity when we have several projects, or several user interface pages, to manage.

     

    Develop once, reuse often has always been our motto when developing with Oracle ADF. We focused our centralizing efforts on a single interface, Human Task Generator (HTG), on which we can:

     

    • Display whichever Application Development Framework (ADF) Faces that components offer, with dependencies between them
    • Process the Human Task with custom or system actions
    • Enable or disable user interaction based on permissions
    • Display process information and help topics so the user has context

     

    To achieve this goal, we developed our HTG based on a custom library (BpmLib) that abstracts BPM's middleware Java classes. In our BpmLib we expose all the necessary methods for getting Human Task payload, replying actions, etc.

     

    In the next sections of this article, we will detail how we have implemented our HTG as well as the BPM library.

     

    Architecture

     

    Our HTG was engineered based on the convention that it should be reusable and extensible, so that if our client's requirements and the application target change, we could easily adjust to new requests. We divided our implementation in three layers, Data, Business and View, as shown in Figure 1.

     

    link-adf-bpm-fig01.jpg

     

    Figure 1: HTG Architecture

     

    The Data Layer encloses two levels of information: BPM's data in which the BPM Suite resides, and the custom-made data model that supports the customers' business logic. In the latter case, the amount and type of information depends on each customer; for example, for some clients we do not need to implement any type of data logic, while for others we have a fairly complex database model.

     

    The Business Layer clusters and abstracts the features and requirements of the HTG. Our BpmLib Library collects the data for each process and its related Human Tasks through BPM's middleware API, exposing it to be consumed by the Model and/or Application Module applications, as shown in Figure 1. In no case do we bypass BPM's middleware API to directly access BPM's data in the data base. By following this rule, we protect our code from any change that may occur in BPM's data structure in future releases of Oracle's BPM product. The Model application exposes data through View Objects. The Application Module application clusters  and exposes data. Here, single methods are also made available to the View Layer.

     

    The View Layer consumes the methods and View Objects exposed by the Business Layer and displays data through the HTG application. HTG can be reused by different types of environments (e.g., ADF applications, ADF Mobile Browser applications, and Mobile Application Framework (MAF) applications). Choose the appropriate HTG interface depending on the application target, each of which is an application deployed as a shared library to allow isolation and reusability. This has two advantages: availability to be deployed as a shared library to any WebLogic Server (WLS) and incorporation into any final ADF application.

     

    The HTGs for ADF, Mobile Browser and MAF have much in common-all of them rely on the same layers of data. Nevertheless, each one has its own technology particularities and specific approaches to be followed. Although we haven't yet developed the HTGs for Mobile Browser and MAF, we've designed our HTG architecture and technical choices with their eventual development in mind.

     

    Custom BPM Library - BpmLib

     

    The BpmLib component is a Java library responsible for all calls to the BPM API. It returns our custom data types, creating an abstraction layer over the BPM API, and also caches and pre-processes data for generating the ADF interface.

     

    Creating an abstraction layer has all the normal advantages, and specifically helps us expose custom methods. The API is very powerful, allowing all kinds of interactions with the underlying engines. But since we need only a subset of those functionalities, there are many options in the API that we don't need. We use the BpmLib to encapsulate those options (e.g., when querying processes and tasks, the API allows us to define which columns we want to be returned) and return the same columns. Another example is the use of flex fields on human tasks. Since we return our own custom-made classes, we include in them the business  data that's stored in the flex fields so that the upper layers don't have to be concerned with the origin of the data. Using our own classes also allows us to flatten the class structure, simplifying access to the other fields.

     

    Because instantiating the service objects of the API is a heavy operation, we cache those objects. However, this raises a problem: this library is running on a different managed server from the SOA and BPM Suite. When the SOA managed server is restarted, the connection becomes invalid, and all calls to the API throw a javax.ejb.EJBException. We had to include a mechanism to catch those exceptions and invalidate the service cache.

     

    The security contexts of the users is also cached data. Users are authenticated by the ADF infrastructure, but with that information we then need to create the security contexts that the BPM API uses. Since this creation is also a heavy operation, we cache these contexts. The cache is a simple collection that stores the contexts of already authenticated users, each entry also associated with a timestamp. Whenever there's a cache access, the timestamp is checked, and if a certain amount of time has passed since the creation of the entry, then that entry is deleted and we create a new context. This way we allow new security  definitions, like group and role membership changes, to be picked up. In future improvements, we may implement a fuller cache or explore other available caching solutions.

     

    Regarding the automatic generation of the Human Tasks interface, this library is responsible for fetching and pre-processing the payload of the tasks so that ADF layers can generate the interface.

     

    On the BPM side, we define the task payload using the base XSD types (like string and date) as well as other custom types, ranging from generic types-like text (to indicate to the interface generator that the field should be rendered as a text area)-to other, more business-specific types-like account, which the generator must know how to render. This also includes types that have to be rendered as several controls. These type of definitions are deployed to MDS, allowing us to share them across processes. So, as we implement more and more processes, the number of new types for each new process rapidly decreases, allowing us to  reduce implementation complexity and time.

     

    On the BpmLib, we created our own Java class representing a payload item, containing the name of the item (a key to a resource file), its value, type, if it is editable or read only, and if it is visible. Lastly, we expose two methods:

     

    • getTaskPayload: returns a list of payload items for a given tasknumber
    • updateTaskPayload: updates the task payload of the task for a tasknumber and a Map<String, Object>

     

    GetTaskPayload Method

     

    The getTaskPayload method starts by calling the following code:

     

    ITaskMetadataService.getTaskDefinition(context,task).getWorkflowConfiguration().getPayload().getMessageAttribute();

     

    It returns a list of MessageAttributeType. Each MessageAttributeType represents one entry of the task payload that we see on the .task file. While this gives us some information (name, type, and whether it's updatable), we also need to get the payload's actual schema, which is available on the MDS; there are several ways to access it. We used the following URL:

     

    http://[host]:[port]/soa-infra/services/[partition]/[composite]/[path_to_file]/[name_of_task]Payload.xsd

     

    We can feed this url to the XSDBuilder...

     

    XMLSchema schema = new XSDBuilder().build(url);

     

    ...and get an object representing the payload. From here, it is just a matter of iterating the MessageAttributeType list, getting for each item the corresponding XMLElement of the payload, and building our own PayloadItem object to return.

     

    UpdateTaskPayload

     

    The updateTaskPayload method receives the values to update in a Map<String, Object> where the String (key) is the name of the payload item, and the Object (value) is its value. The actual class of the Object depends on the type of the payload item. For simple types, it's the closest equivalent in Java to the type (e.g., a String, an Integer, or a Date). For complex types, it's a String for which the XML value needs to be stored. It starts by calling the Task.getPayloadAsElement() method to get an XMLElement with the current payload. Then it also gets the XMLSchema in  the same way as the getTaskPayload. This is needed in order to know the type of each payload item. It then updates the XMLElement with the values passed in the parameter, and calls the ITaskService().updateTask(context, task) method to save the changes.

     

    HTG Transformation Model Engine

     

    The core of the engine where Human Task payloads are transformed is located at the Application Module in the Business Layer, as depicted in Figure 1. Here, we expose the getHumanTaskPayload method as a class available for clients.

     

    We established our core engine here for the following reasons:

     

    • The getHumanTaskPayload method is exposed in the application module and therefore can be invoked independently of the targeted platform (ADF, ADF Mobile Browser or MAF)
    • Deployed as shared library to any Weblogic Server
    • Changes made in this module don’t affect the upper layer and can be deployed as demanded since the contract is not broken
    • Proximity with BpmLib

     

    During payload transformation we are able to support two types of fields:

     

    • Simple: Single ADF components such as input text, select one choice, or input date, etc.
    • Complex: Custom-created components that encapsulate two or more ADF components--for example, a search field that combines an input text and a button, or a custom address component as depicted in Figure 2, where we have an arrangement of several components into a single custom address component.

     

    link-adf-bpm-fig02.png

     

    Figure 2 - Address Complex Type

     

    Our HTG transformation model engine is structured on Java classes to represent Human Taks attributes. The Java classes we have created for that purpose are: HumanTaskAttribute, HumanTaskAttributeFacade and HumanTaskPayload. Their relationship can be viewed in Figure 3.

     


     

    link-adf-bpm-fig03.jpg

     

    Figure 3 - HTG transformation model Engine Java Classes

     

    The HumanTaskAttribute Java class represents each attribute of the Human Task payload regardless of being based on a primitive type, simple type or even complex type. In those cases in which the attribute is a complex type, each XSD element is represented as a HumanTaskAttribute. The variables we have defined for this Java class are:

     

    • name - internal attribute name. If the attribute is a complex type, the name is the concatenation of Human Task's attribute name plus the name of the element inside the XSD.
    • value - the value at which the attribute is to be set.
    • type - contains the type of the attribute, i.e. STRING for string attributes, INTEGER for integer attributes, etc. For complex types, the type is similar to XSD name in order to be easily recognized (i.e., ADDRESS for attributes based on address XSD).
    • isReadOnly - true if the attribute is not editable, otherwise false.
    • isRequired - true if the attribute is mandatory, otherwise false.
    • isHidden - true if the attribute must not be displayed on the interface, otherwise false. This type of attributes behaves like auxiliary variables.
    • isVisible - true if the attribute should be visible, otherwise false. This variable is useful for setting conditional visibility of the attributes on the interface.

     

    The HumanTaskAttributeFacade Java class encloses all attributes of each Human Task payload's attribute, regardless of the type (primitive, simple or complex). If the attribute is based on a complex type, this Java class has as many HumanTaskAttributes as elements in the XSD. If the attribute is based on primitive or simple attributes, this class has only one HumanTaskAttribute attribute. This situation can be viewed in Figure 3. The variables we have defined for this Java class are:

     

    • type – contains the same value as the attribute type in the HumanTaskAttribute Java class. This variable exists only for code simplicity reasons on later data access.
    • attributes – represents the list of HumanTaskAttributes (List<HumanTaskAttribute>).

     

    For this same Java class we have defined four methods:

     

    • getValue - get Human Task's attribute value based on attribute's internal name. If internal name doesn't exist, a NULL is returned.
    • setValue - set the new value for attribute's internal name.
    • hasKey - returns true if Human Task's internal name exists, otherwise returns false.
    • getValues - get all Human Task payload attributes. The returning value is a java.util.Map, where the Key contains the attribute's internal name and the Value the value of the attribute.

     

    Regarding HumanTaskPayload, this Java class encloses all human task payload attributes regardless of its attribute's complexity (primitive, simple or complex type). It has the following variable:

     

    • humanTaskPayloadItems - gathers all data of HumanTask payload attributes

     

    For this same Java class we have defined three methods, based on HumanTaskAttribute's methods:

     

    • getValues - gets all human task payload attributes. The returning value is a java.util.Map where the Key contains the attribute's internal name and the Value the value of the attribute.
    • getValue - gets HumanTask's attribute value based on attribute's internal name. If internal name doesn't exist, a NULL is returned.
    • setValue - sets the new value for attribute's internal name.

     

    In addition to previous Java classes, we have two more with no less importance: HumanTaskUtilities and HumanTaskGeneratorAMImpl. The first one is a utility class that helps parse the XSDs' complex types; the second is the implementation class of the "HumanTaskGenerator" application module where the "getHumanTaskPayload" method is exposed and returns all HumanTask payload attributes after transformation. The Java class HumanTaskUtilities has the following methods to perform the transformation:

     

    • isComplexType – true if the attribute is complex (i.e., attribute based on a XSD), otherwise false.
    • typeHasDropDownValues – true if the attribute is based on a XSD and one of its elements’ name is prefixed with "ddl," otherwise false.

     

    The current attribute's type is determined during iteration overHumanTask payload attributes returned by BpmLib. Depending on the type, one of the following methods is called:

     

    • addSimpleTypeAttribute – If the attribute is simple or primitive, this method is called. In this method, the variables of HumanTaskAttribute and HumanTaskAttributeFacade Java classes are set and added to the top-level class, HumanTaskPayload.
    • addComplexTypeAttribute – If the attribute is complex, this method is called. In this method, we strip the HumanTask attribute's XSDs in order to get its elements, set HumanTaskAttribute and HumanTaskAttributeFacade Java classes variables, and add them to the top level class, HumanTaskPayload.
    • addDropDownValuesToAttribute – If the attribute is complex and the current XSD element being parsed has the prefix "ddl," this method is called. In this method, an extra HumanTaskAttribute entry on the overall structure (see Figure 3, above) is created in order to store the list of items for the control.

     

    HTG Declarative Components

     

    Custom declarative components allow us to build custom components from existing JSF components. They play a major role in showing the same type of data in a desirable layout. Regarding HTG, the declarative components are important for Complex type fields available in human task payloads. By creating this type of component, we can:

     

    • Make use of the primitive ADF Faces components
    • Define custom behavior logic for each new declarative component (e.g., during value change events, etc., of the primitive ADF Faces components)
    • Create new declarative components as needed, without interfering with the others
    • Deploy as shared library to any Weblogic Server

     

    The universe of declarative components already developed for a generic target audience is depicted in Error! Reference source not found, below.

     

                                                                                                                                            
    Declarative ComponentImage
    Addresslink-adf-bpm-fig02.png
    BetweenDateslink-adf-bpm-fig04.png
    SearchModallink-adf-bpm-fig05.png

     

    Our experience tells us that many other declarative components can be developed with different levels of complexity, depending on each customer's data display needs.

     

    HTG Generic Task Attributes Binding

     

    Our HTG is a taskflow that generates, at runtime, a custom interface for HumanTasks. It is organized into three regions:

     

    • global process information regarding the current task
    • payload for the HumanTask
    • comments or attachments.

     

    Inside our HTG, the payload is encapsulated in another taskflow. To use, we just need to drag and drop to our jsf page,which encapsulates all logic defined for the Human Tasks and is responsible for component generation at runtime. The following diagram represents the execution flow performed in our HTG taskflow.

     

    link-adf-bpm-diagram-1.png

     

    Our HTG module is defined by a set of input parameters. These input parameters are used to determine which Human Task to show. Pre-processing is then done in order to get the Human Task payload based on our custom made BpmLib library and determine if the user that is trying to acquire the task has appropriate permissions. After this two steps are completed, each Human Task attribute is rendered according to its type.

     

    The generated Human Tasks have system and custom actions. System actions are equal for all tasks and are those available in BPM Workspace. Custom actions are defined for each task during process development.

     

    Regarding the last step of our execution flow, Runtime Component Design, we have developed a task flow for rendering the Human Task attributes. The task flow is implemented as depicted in Figure 4.

     

    link-adf-bpm-fig07.jpg

     

    Figure 4 - HTG Payload Task Flow

     

    It all starts by getting Human Task payload attributes with previous transformations already done. Then it's time to set attribute visibility, which is related to an attribute's conditional visibility-that is, if the visibility of any attribute is based on another attribute's value, then it is defined at this time. The next step is about getting the values to populate the selectOneChoice ADF Faces component. Last but not least, the HTGPayload fragment is responsible for rendering all Human Task attributes from any possible Human Task, as shown in the following snippet of code.

     

    <?xml version='1.0' encoding='UTF-8'?>      <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"                xmlns:af="http://xmlns.oracle.com/adf/faces/rich"                xmlns:f="http://java.sun.com/jsf/core"                  xmlns:htgDC="/htgDC">                  <af:panelGroupLayout id="pgl1">                    <af:forEach items="#{pageFlowScope.HTGPayload.humanTaskPayloadItems}"                  var="item">                        <af:panelLabelAndMessage id="plm_Text"                                    label="#{item['attributes'][0]['label']}:"                                    visible="#{item['attributes'][0]['isVisible']}"                                    rendered="                      #{item['type'] == pageFlowScope.HumanTaskUtilities.textType and                        item['attributes'][0]['isHidden'] == false and                        item['attributes'][0]['isReadOnly'] == false and                    pageFlowScope.HTGPayload.payloadReadonly == false}"                                    showRequired="#{item['attributes'][0]['isRequired']}"                                    labelStyle="width:210px !important;                                              vertical-align:top">            <af:inputText id="it_Text"                            simple="true"                          autoSubmit="true"                          value="#{item['attributes'][0]['value']}"                          label="#{item['attributes'][0]['label']}:"                          readOnly="#{item['attributes'][0]['isReadOnly'] ||                                      pageFlowScope.HTGPayload.payloadReadOnly}"                          required="#{item['attributes'][0]['isRequired']}"                          visible="#{item['attributes'][0]['isVisible']}">                <f:attribute name="humanTaskAttributeName"                            value="#{item['attributes'][0]['name']}"/>            </af:inputText>          </af:panelLabelAndMessage>                              <af:panelLabelAndMessage id="plm_Decimal"                                    label="#{item['attributes'][0]['label']}:"                                    visible="#{item['attributes'][0]['isVisible']}"                                    rendered="                    #{item['type'] == pageFlowScope.HumanTaskUtilities.decimalType and                      item['attributes'][0]['isHidden'] == false}"                                    showRequired="#{item['attributes'][0]['isRequired']}"                                  labelStyle="width:100px !important">            <af:inputText id="it_Decimal"                            simple="true"                            autoSubmit="true"                          value="#{item['attributes'][0]['value']}"                          label="#{item['attributes'][0]['label']}"                            readOnly="#{item['attributes'][0]['isReadOnly'] ||                                      pageFlowScope.HTGPayload.payloadReadonly}"                          required="#{item['attributes'][0]['isRequired']}"                          converter="ConverterBigDecimal"                          validator="#{ValidatorsHTG.validateDecimalField}">                <f:attribute name="humanTaskAttributeName"                            value="#{item['attributes'][0]['name']}"/>            </af:inputText>          </af:panelLabelAndMessage>                              <af:panelLabelAndMessage id="plm_Date"                                    label="#{item['attributes'][0]['label']}:"                                    visible="#{item['attributes'][0]['isVisible']}"                                    rendered="                        #{item['type'] == pageFlowScope.HumanTaskUtilities.dateType and                          item['attributes'][0]['isHidden'] == false}"                                    showRequired="#{item['attributes'][0]['isRequired']}"                                  labelStyle="width:210px !important">            <af:inputDate id="id_Date"                            simple="true"                            autoSubmit="true"                          columns="10"                          value="#{item['attributes'][0]['value']}"                          label="#{item['attributes'][0]['label']}"                            readOnly="#{item['attributes'][0]['isReadOnly'] ||                                      pageFlowScope.HTGPayload.payloadReadonly}"                          required="#{item['attributes'][0]['isRequired']}"                          validator="#{ValidatorsHTG.validateDateField}"                          converter="javax.faces.DateTime">                <af:convertDateTime hintDate="DD-MM-AAAA"/>                <f:attribute name="humanTaskAttributeName"                            value="#{item['attributes'][0]['name']}"/>            </af:inputDate>          </af:panelLabelAndMessage>                          <af:panelLabelAndMessage id="plm_Address"                                    visible="#{item['attributes'][0]['isVisible']}"                                    rendered="                        #{item['type'] == pageFlowScope.HumanTaskUtilities.address and                          item['attributes'][0]['isHidden'] == false}"                                  showRequired="#{item['attributes'][0]['isRequired']}">            <htgDC:Address id="adress_Address"                            countryLabel="Country:"                            countryValue="#{item['attributes'][0]['value']}"                            countryReadOnly="#{item['attributes'][0]['isReadOnly'] ||                                            pageFlowScope.HTGPayload.payloadReadonly}"                            countryRequired="#{item['attributes'][0]['isRequired']}"                            countrySelectItems=""                            addressLabel="Address:"                            addressValue="#{item['attributes'][1]['value']}"                        addressReadOnly="#{item['attributes'][1]['isReadOnly'] ||                                        pageFlowScope.HTGPayload.payloadReadonly}"                        addressRequired="#{item['attributes'][1]['isRequired']}"                        stateLabel="State:"                        stateValue="#{item['attributes'][2]['value']}"                        stateReadOnly="#{item['attributes'][2]['isReadOnly'] ||                                        pageFlowScope.HTGPayload.payloadReadonly}"                        stateRequired="#{item['attributes'][2]['isRequired']}"                        zipCodeLabel="Zip Code:"                        zipCodeValue="#{item['attributes'][3]['value']}"                        zipCodeReadOnly="#{item['attributes'][3]['isReadOnly'] ||                                        pageFlowScope.HTGPayload.payloadReadonly}"                        zipCodeRequired="#{item['attributes'][3]['isRequired']}"                        postalCodeLabel="Postal Code:"                        postalCodeValue="#{item['attributes'][4]['value']}"                        postalCodeReadOnly="#{item['attributes'][4]['isReadOnly'] ||                                        pageFlowScope.HTGPayload.payloadReadonly}"                        postalCodeRequired="#{item['attributes'][4]['isRequired']}"                        humanTaskAttributeName="#{item['attributes'][0]['name']}"                        panelLabelStyle="width:100px !important;                                        vertical-align: top !important;"/>      </af:panelLabelAndMessage>              </af:forEach>          </af:panelGroupLayout>      </jsp:root> 

     

    Note: This task flow can be enhanced when you have an ADF Faces control and need to search information in modal in order to set Human Task attribute values.

     

    In the HTGPayload fragment, an iteration over all Human Task payload attributes is performed; depending on its type, it will be rendered by one of the underlying components available from the set of components inside the "af:foreach" ADF Faces component. Any new component needed should be put here.

     

    Each component is populated based on the data structure previously presented to represent all Human Task payload attributes, regardless of complexity. For more details about how to assign component properties, you can find an example here.

     

    HTG Generic Task Attribute Validation

     

    There are three types of task validations:

     

    1. Simple Component level (e.g., input fields for numeric types only)
    2. Custom Component level (e.g., dependent fields in a custom component)
    3. Task submission level (e.g., submission of the task form with validation rules for individual components that cannot be filled)

     

    Simple Component validation is defined in the "validator" component's property, as shown below:

     


    <af:panelLabelAndMessage id="plm_Date"
                              label="#{item['attributes'][0]['label']}:"
                              visible="#{item['attributes'][0]['isVisible']}"
                              rendered="#{item['type'] == pageFlowScope.HumanTaskUtilities.dateType and item['attributes'][0]['isHidden'] == false}"
                              showRequired="#{item['attributes'][0]['isRequired']}"
                              labelStyle="width:210px !important">
       <af:inputDate id="id_Date"
                     simple="true"
                     autoSubmit="true"
                     columns="10"
                     value="#{item['attributes'][0]['value']}"
                     label="#{item['attributes'][0]['label']}"
                     readOnly="#{item['attributes'][0]['isReadOnly'] ||
                                 pageFlowScope.HTGPayload.payloadReadonly}"
                     required="#{item['attributes'][0]['isRequired']}"
                     validator="#{ValidatorsHTG.validateDateField}"
                     converter="javax.faces.DateTime">
         <af:convertDateTime hintDate="DD-MM-AAAA"/>
         <f:attribute name="humanTaskAttributeName" value="#{item['attributes'][0]['name']}"/>
       </af:inputDate>
      </af:panelLabelAndMessage>

     

    To centralize all validations of this type, we have followed a recommended best practice in ADF by creating the "ValidatorsHTG" Java class. This class is instantiated with session scope in the "Managed Beans" tab of the faces-config.xml file. The code snippet below shows two component validations of two different types in the "ValidatorsHTG" Java class.

     

         
         public class ValidatorsHTG {  
               
             public ValidatorsHTG() {  
             }  
               
             public void validateDecimalField(FacesContext facesContext, 
                                              UIComponent uIComponent, 
                                              Object object) {  
                 if(object != null){  
                     String tempStr = object.toString();  
                       
                     if(!Utils.isLong(tempStr))  
                         throw new ValidatorException(
                          new FacesMessage("Validation Error", "Required a numeric value"));  
                     else {  
                         Long number = Long.valueOf(tempStr);  
                         if(number < 0)  
                             throw new ValidatorException(
                         new FacesMessage("Validation Error", "Required a positive value"));  
                     }  
                 }   
             }  
               
             public void validateDateField(FacesContext facesContext, 
                                           UIComponent uIComponent, 
                                           Object object) {  
                 if(object != null) {  
                     long currentTime = System.currentTimeMillis();  
                     java.util.Date tmp1 = (java.util.Date)object;  
                     long inputTime = tmp1.getTime();  
                       
                     Object[] formatArgs = new Object[1];  
                     formatArgs[0] = ((RichInputDate)uIComponent).getLabel();  
                     if(inputTime > currentTime)  
                         throw new ValidatorException(
         new FacesMessage("Validation Error", "The date must be greater than the current date"));  
                 }  
             }  
         }  
    

     

    Custom Component validation can be done in two ways: in the "validator" component property of the declarative component, basic validation, or in custom classes for more complex validation. For example, imagine that the Address declarative component is valid only if the "Country" field is filled as well as "Address" and "Postal Code"; if not, an error message is raised to the front end. For this kind of behavior, we have developed the "HTGComplexTypesValidation" Java class, where all generic validations will be available for future reuse. The next code snippet shows the Address complex-type validation:

     

         public class HTGComplexTypesValidation extends HTGValidations {  
               
             public HTGComplexTypesValidation(HumanTaskPayload payloadValues, 
                                              String processName, 
                                              String humanTaskName, 
                                              String humanTaskOutcome) {  
                 super(payloadValues, processName, humanTaskName, humanTaskOutcome);  
             }  
               
             public boolean validateAddressComplexType(List messages) {  
                 boolean valid = true;  
                   
                 //address_country - internal human task payload attribute  
                 //address_address - internal human task payload attribute  
                 //address_postalCode - internal human task payload attribute  
                 Object country = payloadValues.getValue("address_country");  
                 Object address = payloadValues.getValue("address_address");  
                 Object address_state = payloadValues.getValue("address_postalCode");  
                   
                 if(Utils.isNullOrEmpty(country))   
                 {  
                     boolean validAttribute = 
                            displayErrorMessage(messages, "The Country is required");  
                     valid = valid && validAttribute;  
                 }  
                 else if (!Utils.isNullOrEmpty(country))   
                 {  
                     if (Utils.isNullOrEmpty(address))   
                     {  
                         boolean validAttribute = 
                            displayErrorMessage(messages, "The Address is required");  
                         valid = valid && validAttribute;  
                     }  
                       
                     if (Utils.isNullOrEmpty(address_state))   
                     {  
                         boolean validAttribute = 
                            displayErrorMessage(messages, "The Postal Code is required");  
                         valid = valid && validAttribute;  
                     }  
                 }  
                   
                 return valid;  
             }  
         }
    
    
    

     

    This approach encloses component behavior and complexity in one place and thus promotes more and better reuse.

     

    Finally, validation at task submission reuses complex-type validations as well as any other validation. The developer has full freedom and control to do any validation for any Human Task for any BPM process. The validations engine is structured as depicted in Figure 5, below, where "ProcessToValidateA" and "ProcessToValidateB" are Java classes of real BPM processes.

     

    link-adf-bpm-fig08.jpg

     

    Figure 5 - HTG Validation Java Classes Architecture

     

    HTGValidations is the top-level Java class called by the user just before replying to the human task. The Human Task is replied only if all attributes (simple or complex attributes) and overall dependencies between attributes are valid. The method "validateHumanTask" is the main method called to perform the validation of the Human Tasks process.

     

         package htg.payload;  
           
         import htg.common.HumanTaskPayload;  
           
         import htg.view.ADFUtils;  
           
         import java.util.ArrayList;  
         import java.util.List;  
           
         public class HTGValidations {  
               
             protected HumanTaskPayload payloadValues;  
             protected String processName;  
             protected String humanTaskName;  
             protected String humanTaskOutcome;  
               
             public HTGValidations(HumanTaskPayload payloadValues, 
                                   String processName, 
                                   String humanTaskName, 
                                   String humanTaskOutcome) {  
                 this.payloadValues = payloadValues;  
                 this.processName = processName;  
                 this.humanTaskName = humanTaskName;  
                 this.humanTaskOutcome = humanTaskOutcome;  
             }  
           
             public boolean validateHumanTask() {  
                 List messages = new ArrayList();  
                 boolean result = true;  
                   
                 if (processName.equalsIgnoreCase("PROCESS_NAME_A"))  
                     result = new ProcessToValidateA(payloadValues, processName, humanTaskName, 
                        humanTaskOutcome).validateHumanTask(messages);  
                 else if (processName.equalsIgnoreCase("PROCESS_NAME_B"))  
                     result = new ProcessToValidateB( payloadValues, processName, humanTaskName,
                        humanTaskOutcome).validateHumanTask(messages);  
                 //classes for new processes wih new human tasks should be introduced here  
                   
                 displayValidationErrorMessages(messages);  
                 return result;  
             }  
           
             private void displayValidationErrorMessages(List messages) {  
                 for (String msg : messages)  
                     ADFUtils.displayInfoMessage(msg);  
             }  
               
             protected boolean displayErrorMessage(List messages, 
                                                   String errorMessage) {  
                 messages.add(errorMessage);  
                 return false;  
             }  
         } 
    
    
    

     

    Human Task validation is made inside of each process Java class created for the purpose, as shown in the next code snippet. For example, inside "ProcessToValidateA" Java class, the Human Tasks for this process are validated.

     

         
         public class ProcessToValidateA extends HTGValidations {  
               
             public ProcessToValidateA(HumanTaskPayload payloadValues,   
                                      String processName,  
                                      String humanTaskName,   
                                      String humanTaskOutcome) {  
                 super(payloadValues, processName, humanTaskName, humanTaskOutcome);  
             }  
               
             public boolean validateHumanTask(List messages) {  
                 boolean result = true;  
                   
                 if (humanTaskName.equalsIgnoreCase("HUMAN_TASK_A"))  
                     result = validateHumanTaskA(messages);  
                 else if (humanTaskName.equalsIgnoreCase("HUMAN_TASK_B"))  
                     result = validateHumanTaskB(messages);  
                   
                 return result;  
             }  
               
             private boolean validateHumanTaskA(List messages) {  
                 boolean valid = true;  
                   
                 HTGComplexTypesValidation complexValidation = 
                              new HTGComplexTypesValidation(payloadValues,
                                                            processName,
                                                            humanTaskName,
                                                            humanTaskOutcome);  
                 valid = complexValidation.validateAddressComplexType(messages);  
                 //etc..  
                   
                 return valid;  
             }  
               
             private boolean validateHumanTaskB(List messages) {  
                 boolean valid = true;  
                   
                 HTGComplexTypesValidation complexValidation = 
                              new HTGComplexTypesValidation(payloadValues, 
                                                            processName, 
                                                            humanTaskName, 
                                                            humanTaskOutcome);  
                 valid = complexValidation.validateAddressComplexType(messages);  
                 //etc..  
                   
                 return valid;  
             }  
         }  
    
    

     

    HTG Generic Task Attributes Conditional Visibility

     

    There may be cases in which the visibility of certain Human Task attributes is based on another attribute's value. Conditional visibility has two setting points: one on entering the "htg-payload-flow.xml" task flow (Figure 4, above), and another in any value change listener of any ADF Faces component or declarative component.

     

    Regarding the first setting point, before the Human Task is displayed, the attribute's conditional visibility must be checked. Why? Imagine the Human Task has already been edited and there is a dependency between attributes. In this case, when re-entering the Human Task it is necessary to gather the value of the attribute that causes the other attribute to change its visibility.

     

    Human task conditional visibility is developed so that each Human Task of each process has its own conditional rules of visibility. This approach promotes flexibility to define attribute visibility.

     

    Real-World Benefits

     

    In this section, we try to answer some basic questions:

     

    • How useful can our approach be in the development process of a new application?
    • How reusable can it be?
    • Is there a real-world benefit to spending more time at the beginning implementing a generic methodology as opposed to following the way Oracle developed its product?

     

    (1) How useful can our approach be in the development process of a new application?

     


     

    Since we developed our HTG in well-defined building layers (Data Layer, Business Layer and View Layer) where each one communicates with the nearest layer, they can be developed independently of the others.

     

    Each project inside of each layer is available as a “Shared Library” and is therefore deployed independently to the WLS. Apart from the advantage described earlier, it is not necessary to deploy all the projects to the server if only one has really changed.

     

    When a new application is ready to be developed independently of the business area, and needs to provide Human Tasks interfaces, the developer simply imports the library into the application and easily drags-and-drops the HTG wherever is needed. It behaves as a regular ADF Faces component.

     

    The user will face a standardized interface for all Human Tasks independently of the process. The developer will have fewer interventions regarding layouts, forgotten fields, etc., and therefore is less likely to commit an error.

     

    (2) How reusable can it be?

     

     

     

    Implementing the HTG for three different customers in different business areas provided us with insight into the level of reusability of our engineered solution. Table 2, below, agglomerates the data by customer's business area, as well as the total number of processes and human tasks created using HTG versus Human Tasks created from scratch.

     

                                                                                                                                                                                                                                                                                                                                                                              
    RetailFinanceEnergyTotal
    # Processes369348
    # Human Tasks96345135
    Human Task Generator (HTG)81345120
    Custom Human Tasks150015
    Percentage of Reusability84.4%100%100%88.9%
    Table 2 - HTG Percentage of Reusability

     

    This table depicts the success of using our solution. In the Finance industry, the HTG satisfies all customer needs. In the Retail sector, there is a high percentage of satisfaction, and in Energy customer needs are satisfied (but only a few small processes were implemented).

     

    One of the leading reasons for these results is our ability to evolve Complex Component Types as the customer needs them, with the underlying validations for each component and the overall human task. The modularization of our HTG in different layers was another advantage, since we never mixed business logic inside viewing data, and vice versa.

     

    (3) Is there a real-world benefit to spending more time at the beginning implementing a generic methodology as opposed to following the way Oracle developed its product.

     

     

     

    All our customers wanted their own custom-made workspace, integrated within a single application, with a customized look-and-feel, and custom business rules associated in the process and interface. In some cases, we started using the available reusable task flows of the workspace, but after a while we weren't able to fulfil customer requirements. They wanted to get the list of tasks and of processes with a specific look-and-feel and with search fields that would make sense for business. Since they had a lot of h Human Tasks, they also wanted an interface that could generate any Human Task interface in order to achieve higher  productivity.

     

    Our HTG and all related libraries were made possible by the extensibility of the ADF framework and BPM's middleware, as well as its productivity features (like declarative components).

     

    From this starting point, we needed to develop a library to interact with the BPM's middleware, the BpmLib. Once we developed our own library, and after some experiments in developing Human Task interfaces, we saw that developing a custom, hand-made interface to generate Human Tasks was not so far away.

     

    After understanding all these facts, we conducted some tests to compare our HTG with generating Human Task interfaces by using Oracle's out-of-the-box approach. The results are based on our customers' requirements and the level of customization needed to achieve the same final results in both cases:

     

                                                                                                                                                                                                                                                                                                          
    Time spent setting a human task interface without using HTG
              (In Hours)
    Time spent setting a human task interface using HTG
              (In Hours)
    Generate Interface (ADF Taskflow based on human task)00
    Customize Interface (Create a new taskflow for each human task with clients customizations)30
    Implement Validations21
    Integration of the human task with the main application10
    Data submission10
    Total71
    Table 3 - Spent time setting a human task interface using HTG versus without using

     


     

    As Table 3 depicts, it takes 6 fewer hours to develop each new Human Task interface, independently of the process, by using HTG.

     

    We also achieved cost-effectiveness by using HTG instead of the regular approach:

     

                                                                                                                                                                                                      
    Using HTGWithout HTG
    Total human tasks that used HTG120
    Total time spent to develop each human task interface
              (In Hours)
    17
    Total time spent to develop all human tasks interfaces120840
    Cost-Effective (In Hours)85.7%
    Table 4 - Cost-Effectiveness of Using HTG

     

    As you can see, using our engineered solution during development lowered costs by 85.7% hours, with fewer interventions, thereby lowering the probability of development errors.

     

    Conclusions

     

    Based on our experience on major projects in different industries, we engineered an accelerator to automatically generate the interfaces for Human Task processes—with a resulting 85.7% decrease in implementation time.

     

    To achieve our main goal, we developed the HTG based on a custom library, BpmLib, that abstracts interactions with the BPM’s middleware. We followed the convention that HTG should be reusable and extensible beyond the application, project or customer.

     

    We structured our HTG in three layers—data, business and view—each with a well-defined scope and purpose so that future changes or enhancements could be easily made.

     

    The entire module—BpmLib, Model, Application Module and HTG—is decoupled from the application business logic, and thus can be used in other BPM applications. Not only can the entire module be reused, but so can its components, independently (BPMLib, Model-AppModule).

     

    In conclusion, this approach of developing a reusable module for Human Task generation is feasible only if we know at the beginning that a significant amount of processes/ Human Tasks need to be developed. After the module is developed, every change is centralized—allowing for significantly reduced development time and improved ROI.

     

    About the Authors

     

    Diogo Henriques is a senior BPM implementation specialist involved in several solutions, including a few large scale projects.

     

    Danilo Manmohanlal is a senior ADF developer working in solutions delivery on retail and finance industry.

     

    Pedro Gabriel is a senior ADF and BPM implementation specialist involved in world wide solutions.

     

    NOTE: This article represents the expertise, findings, and opinion of the authors.  It has been published by Oracle in this space as part of a larger effort to encourage the exchange of such information within this Community, and to promote evaluation and commentary by peers. This article has not been reviewed by the relevant Oracle product team for compliance with Oracle's standards and practices, and its publication should not be interpreted as an endorsement by Oracle of the statements expressed therein.