Skip to main content
Skip table of contents

Introduction

There is a standard way to implement a TrustBuilder configuration including usual locations to store files and scripting best practices.

These are outlined here and it is advised to follow these guidelines to assist with handover between consultants and ease of support.

In short the guidelines cover:

  • File and directory structure

  • Workflow organisation

  • Activity purpose and relationship

Directories and Files - TB_HOME

There is one TBHOME for each application, this can be shared over different trustbuilder instances (across application servers and physical servers). This is the recommended way to organise a TBHOME.

Items marked with (*) are optional.

CODE
TB_HOME/
TB_HOME/config.xml
TB_HOME/logback.xml (*)

TB_HOME/myAppWorkflow.xml
TB_HOME/myAppWorkflow
TB_HOME/myAppWorkflow/scripts
TB_HOME/myAppWorkflow/templates (*)
TB_HOME/myAppWorkflow/templates.js (*)

TB_HOME/common
TB_HOME/common/scripts
TB_HOME/common/templates (*)
TB_HOME/common/templates.js (*) 

In this case myAppWorkflow.xml is a workflow. The directory myAppWorkflow contains everything that is used by that workflow such as script and templates. If there are common scripts and templates that are used by different workflows then it is the norm to place them in the common directories.

All file names should be descriptive. For instance if there is a script that contains functions that deal with adapter responses name the script something like myApp_adapter-responses.js thus relating the scripts to the workflow and describing the general purpose.

The workflow ID that is configured in TBA does not need to be the same as the workflow file name, but it allows easier mapping between url and workflow.

Workflows and Sub-workflows

An normal workflow can be a parent or a sub-workflow. If a workflow is marked as being Only Sub-workflow within TBA under the workflow section of configuration.

Once marked as Only Sub-workflow a workflow cannot be used as a parent and call other workflows or be a standalone workflow it must be called by another.

Templates

There are two ways to create templates that can be read and populated by scripts.

Separate files with extensions of .htm, .html or .tmpl can be created or variables can be set within Javascript.

It is recommended that when the template is large or contains an amount of formatting markup such as HTML which will likely be used by another department (such as the design department) that this be a template file with extension of .htm .html or .tmpl.

If the template is to be shared with a design department to control the layout then it is more common that a .html extension be used to ease sharing.

If the template is forming a request such as a SOAP message or XML that is likely to be used internally to call another adapter or external service then it is more common that these templates are created in script files as variables.

Example of Templates Defined as Variables

CODE
var Response = '<RESPONSE> \
    <USERNAME>$username</USERNAME><PASSWORD>$password</PASSWORD> \
</RESPONSE>';

var Error = '<ERROR> \
               <STATUS>$status</STATUS> \
               <MESSAGE>$message</MESSAGE> \
             </ERROR>'; 

Static Files

Static files are files such as images that can be referenced by the standalone templates.

Any static files need to be stored in a specific directory. Specify the folder where all the static files are stored in the STATIC_FOLDER jndi environment variable.

The value of the STATIC_FOLDER variable is an absolute path to a directory on the server.

In WebSphere this can be set when installing the TrustBuilder application In Tomcat it can be set in context.xml thus:

CODE
<Environment name="STATIC_FOLDER" value="/opt/securit/tb\_home/static"/>

Scripting Guidelines

Adapter Activities

When preparing a request to be sent to an adapter activity the script should be set in the Before Adapter Script function. Not in an activity that comes before the adapter activity such as a script contained within a condition activity.

For instance there is a JDBC adapter activity.

A select request must be created that is used by the adapter to select from a database.

This select request should be created in a function defined in the Before Adapter Script of the JDBC adapter activity itself not in any other activity.

Sub-workflow Activity

A sub-workflow should be used to separate blocks of logic or to allow for code reuse.

As for the adapter activity and preparation of data that is to be sent into the sub-workflow should be created within the sub-workflow Before Worfkow Activity Script function of the sub-workflow activity itself. Not in any other activity such as a preceding condition activity.

There is no restriction on how a workItem is used within a sub-workflow. The parent workflow workItem can be re-used or not. This is specified in the sub-workflow activity properties.

Code Reuse

Try to reuse code as much as possilble, this is good practice in any scripting language. If for instance it is noted that some lines of code are written many times with maybe just one variable changing then this code can be moved to a function that accepts an argument for the changing variable.

Then wherever this code is required the function is called.

For instance.

Bad Practice: Code Not Being Reused

CODE
function scriptFunction1(workItem) {
    var credHeader = workItem.input.parameter("iv-creds"), 
        arrCred = credHeader.split(", "), 
        credential = arrCred[1];
}

function scriptFunction 2(workItem) {
    var credHeader = workItem.input.parameter("iv-creds"),
        arrCred = credHeader.split(", "),
        credential = arrCred[1]; } 

Good Practice: Code Refactored for Reuse

CODE
function scriptFunction1(workItem) {
    var cred = getCredential(workItem.input.parameter("iv-creds"));
}

function scriptFunction2(workItem) {
    var cred = getCredential(workItem.input.parameter("iv-creds"));
}

function getCredential(header) {
    var arrCred = header.split(", ");
    return arrCred[1];
} 
Function formatting

Mandatory

This guideline is mandatory to get your script function working, it is important that the curly brace sits on the same line as the function definition. This is in order for the functions to be discoverable and selectable in a property box of an activity.

Global and Local Variables

In Javascript a global variable is one that is created outside of any containing function or a variable that is created in a containing function but not preceded with the var keyword.

Global variables should be used with caution as they are overwritten this can be an advantage but can be difficult to monitor and can easily lead to bugs.

For instance if a global variable is set in scriptA and a global variable is set in scriptB with the same name. If scriptB is loaded by the engine first then the value of the variable set in scriptA will be used as the value for the global variable.

Global and Local Variables

CODE
var globalOne = "hello";

function setVar(){
    globalOne = "goodbye"; //change the value of a global variable
    var localOne = "local hello"; //create a local variable can only be read within this function
    thinksLocal = "this is also a global"; //this is not local it is global, not preceded with var
}

alert(globalOne); //reads hello

setVar();

alert(globalOne); //reads goodbye

alert(thinksLocal); //can be read is global although has been set within a function

try{
    alert(localOne); //this is undefined and throws an error it is a local variable
}
catch(e){
    alert(e);
}
SQL and String Concatenation

Creating a query to send to a database using string concatenation exposes a script to potential SQL injection attacks. Where a string that is set as a variable can contain SQL that is then executed against the database.

To avoid this prepared statements and query parameters should be used.

A prepared statement is the query itself with values defined as question marks. The query parameters are the actual values that replace the question marks when the query is run.

When using this technique the values are validated before being sent to the database.

Improper Creation of an SQL Query

CODE
function prepareJdbcReq(workItem) {
    username = workItem.input.parameter("user");
    passwd = workItem.input.parameter("pwd");
    sql = "SELECT \* FROM users WHERE user ="+username+" AND password = "+passwd + ";";
    workItem.dbInput = tb.jdbcSelectRequest(sql);
} 

Correct Creation of an SQL Query with properly scoped variables

CODE
function prepareJdbcReq(workItem) {
    var username = workItem.input.parameter("user"),
        passwd = workItem.input.parameter("pwd"),
        sql = "SELECT \* FROM users WHERE user = ? AND password = ? "; 
    workItem.dbInput = tb.jdbcSelectRequest(sql, [username, passwd]);
} 
Type Safety

Javascript is a type less language. Variable types do not need to be defined as they do in other languages such as Java. So a variable can be an integer or a string for instance.

To make a genuine comparison in a condition type save comparitors should be used. These take the format of a triple character set such as === as opposed to a non type safe comparison ==.

Comparing with non type safe can be used if that is the desired outcome. Javascript will attempt to cast the object being compared to that which is expected but it can have undesired effects for example when a boolean is a string.

CODE
var thisMayBeTrue;
thisMayBeTrue = 1;

alert(thisMayBeTrue == true); //=\> true the 1 is cast to boolean true by Javascript
alert(thisMayBeTrue === true); //=\> false the value is not a boolean true

thisMayBeTrue = '1';

alert(thisMayBeTrue == true); //=\> true the value is cast from a string to a boolean
alert(thisMayBeTrue === true); //=\> false the value is not a boolean true

thisMayBeTrue = true;

alert(thisMayBeTrue == true); //=\> true no cast needed alert(thisMayBeTrue === true); //=\> true this is a boolean true 

thisMayBeTrue = 'true';

alert(thisMayBeTrue == true); //=\> false no cast from string to boolean.
alert(thisMayBeTrue === true); //=\> false as expected not a boolean true 
WorkItem Object

It is good practice to keep a clean workItem object throughout the scripts. The best approach is to scope variables into sub objects.

For example:

Poor WorkItem Organisation

CODE
function SomeScriptfunc(workItem) {
    workItem.user = workItem.input.parameter("user");
    workItem.pwd = workItem.input.parameter("pwd");
}

Good WorkItem Organisation

CODE
function SomeScriptFunc(workItem) {
    workItem.credentials = { 
        user: workItem.input.parameter("user"),
        pwd: workItem.input.parameter("pwd")
    }; 
} 
Use Object Constructors

It is more performant to create an object with properties defined and populated as opposed to creating an object and then setting properties and values.

Poor Object Creation

CODE
function SomeScriptfunc(workItem) {
    workItem.req = {};
    workItem.req.user = workItem.input.parameter("user");
    workItem.req.pwd = workItem.input.parameter("pwd");
}

Better Object Creation

CODE
function SomeScriptfunc(workItem) {
    workItem.req = {
        user: workItem.input.parameter("user"),
        pwd: workItem.input.parameter("pwd")
    };
}
Comments

All functions should be commented thus:

CODE
/*
    Description: A descriptive purpose of the function, what it does. 
    Previous Activity: The activity that was run before this activity was called, if relevant.
    Next Activity: The next activity after this script is run.
*/
function someScriptFunc(workItem) {
    // This is inline comment to describe what is happening within the function
} 

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.