Overview

Skill Level: Advanced

This recipe requires advanced understanding of IBM BPM / IBM BAW.

This recipe will teach you how to design maintainable, reliable and future-proof IBM Business Process Manager / IBM Business Automation Workflow client side human services efficiently.

Ingredients

This recipe was tested with IBM BPM 8.5.7 CF2016.12 and IBM BAW 18.0.0.1.

Step-by-step

  1. Common approach to building client side human services and associated problems

    The most common approach to building client side human services (CSHS) is to directly place script or server call steps in the service diagram. The script steps usually perform the following:

    • script step A: initialize CSHS variables,
    • script step B: respond to a specific control event and update CSHS variables,
    • script step C: process server call step,
    • script step D:¬†respond to a specific control event and update CSHS variables,

    It is important to note that the contents of the above script steps are not part of an object, but are standalone and do not share any common functions.

    Such simplicity may be inviting to apply and not much consideration is required to start development, but it will only work when there are no more then a few steps required to implement a CSHS. For real world applications, sooner or later such an approach will result in an implementation which no one can understand and maintain, especially if a CSHS was implemented by more than one developer. For example:

    bad_end_result-1

    Each of the yellow script steps above contains a code fragment; when the number of script steps in a CSHS becomes large, it is practically impossible to update either one without affecting every other script step. When performing updates, a developer must first review each script step, remember what it does and how it must be changed, implement the changes and test them. Adding new steps may also be difficult because the real estate may simply not be available, as in the above example.

    The end result is thus time consuming and extremely error prone to maintain.

  2. Could there be a better approach to building client side human services?

    A possible better approach to building CSHS is to apply some kind of a model-view-presenter design pattern. We already have a model (CSHS variables) and a view (coach), the only thing missing is a presenter. While a CSHS is itself a kind of a presenter, it is not object-oriented, and an experienced developer would prefer to have at least the presenter as an object and not as a diagram. In what follows we will specify a better design compared to the commonly used one. An important design consideration is that loading CSHS from saved context must be supported.

    The design pattern consists of the following main tasks:

    1. CSHS diagram design pattern definition,
    2. presenter definition,
    3. mapping of diagram steps and presenter functions.

    The first task is to define a common design pattern for CSHS diagrams:

     diagram_design-3

    The above diagram contains the following design elements:

    • the “init” script step will initialize the presenter,
    • the “refresh” script step will mostly deal with updating the visibility of view elements,
    • control event handlers handle button clicks and similar¬†(e.g. handleButton1, handleButton2),
    • all control handlers which don’t complete the task are placed one below another, flow from right to left and are joined with the “refresh” step – see the green arrow,
    • control handlers which complete the task should flow from left to right towards the “End” event, as per the red arrow.

    The second task¬†is to define the presenter as a standard JavaScript object, with the same name as the CSHS, and place it in the “init” script step:

    if(typeof ClientSideHumanServiceDesignPattern == "undefined"){
    /*
    Presenter design must consider the following distinct
    cases and their combinations:
    1) a CSHS was just loaded,
    2) a CSHS was just loaded from a saved context,
    3) a CSHS using this presenter has already
    been displayed in the current task window.
    */
    ClientSideHumanServiceDesignPattern = {
    /*
    Do not define presenter properties here
    because they will be shared between
    all CSHS which use(d) this presenter in
    the current task window. This may be
    a problem for nested CSHS of the same type,
    or when displaying CSHS in a loop. Instead,
    use "this.getLocal" and "this.setLocal",
    which will also enable persistence.
    */

    init: function() {
    if(!tw.local.initialized) {
    this.initModel();
    this.initPresenterLocal();
    tw.local.initialized = true;
    }
    },

    /*
    To be used if there is a need for presenter properties
    without proliferating CSHS variables. If client side performance
    becomes a problem, use CSHS variables instead.
    */
    setLocal: function(name, value) {
    if(!tw.local.presenterLocal) tw.local.presenterLocal = "{}";

    var local = JSON.parse(tw.local.presenterLocal);
    local[name] = value;
    tw.local.presenterLocal = JSON.stringify(local);
    },

    /*
    To be used if there is a need for presenter properties
    without proliferating CSHS variables. If client side performance
    becomes a problem, use CSHS variables instead.
    */
    getLocal: function(name) {
    if(!tw.local.presenterLocal) return null;

    var local = JSON.parse(tw.local.presenterLocal);
    if(name in local) {
    return local[name];
    } else return null;
    },

    initModel: function() {
    tw.local.refreshCounter = 0;
    tw.local.initializationDate = null;
    },

    initPresenterLocal: function() {
    this.setLocal("initializationDate", new Date());

    // Just a few tests
    var simpleArray = [];
    simpleArray[0] = "test";
    this.setLocal("simpleArray", simpleArray);

    var arrayOfObjects = [];
    arrayOfObjects[0] = {name: "name", value: "value"};
    this.setLocal("arrayOfObjects", arrayOfObjects);

    this.setLocal("nothing", null);
    },

    shouldDoSomething: function() {
    return true;
    },

    doSomething: function() {
    // Additional handler code here
    },

    handleButton1: function() {
    // Control handler code here
    },

    refresh: function() {
    tw.local.refreshCounter++;

    // We would like to display this local variable on coach
    tw.local.initializationDate = this.getLocal("initializationDate");
    }
    }
    }

    ClientSideHumanServiceDesignPattern.init();

    The third task is to map diagram steps and presenter functions: 

    Definition of the “refresh” script step:

    ClientSideHumanServiceDesignPattern.refresh();

     An example of control handler script step:

    ClientSideHumanServiceDesignPattern.handleButton1();

    ¬†“shouldDoSomething?” gateway:

    ClientSideHumanServiceDesignPattern.shouldDoSomething()

    gateway

    The step name should¬†reflect the presenter’s function used.

  3. What is the experience with the explained approach?

    The experience with the explained approach is that it brings the following benefits:

    • Increased CSHS diagram readability,
    • adding additional¬†diagram steps does not require a developer to rearange existing steps – simply add new steps below existing steps,
    • if it is necessary for a view to respond to control handlers or data changes, this may be handled in the “refresh” step, in addition to the code already in other steps,
    • the entire, or most of the, CSHS code is available in one location, making it easy to comprehend, maintain and test,
    • if a particular control handler’s path should save the context of a CSHS (the wire leading to the “ToRefresh” gateway has the “Save execution context” option enabled), the suggested approach supports this requirement – the restored CSHS will continue directly to the “init” step where the presenter object will be recreated,
    • increased reliability of client side human services.

     

  4. Security aspect

    The explained aproach¬†increases the security exposure of client side human service code when considering browser power users. While in any case the code is running on client side and should therefore not be trusted, making things worse is certanly not desirable. Also, there is no way around the same problem if we were to require common client side scripts from Web files. As per IBM’s explanation when I raised this issue with them: such is the nature of client side JavaScript and where security is a concern, server side controls must be put in place.¬†

    It is possible to define the presenter object directly in the CSHS “tw” context variable, where it can be used the same way as explained:

    tw.ClientSideHumanServiceDesignPattern = {...}

    tw.ClientSideHumanServiceDesignPattern.init();

     

    Such an approach does not change the default security exposure of CSHS, but it is unpredictable in a sense that, while it may be working in BPM 8.5.5.7 and BAW 18.0.0.1, this may change in future releases. So I have opened a RFE requesting for the current behavior to remain the same in future releases, or that some other approach is suggested:

    http://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=134004

    Please make sure to vote for the feature request.

  5. Conclusion

    Author: Aleksander Kovańć

Join The Discussion