(Quick Reference)

4 Developer's Guide - Reference Documentation

Authors: Stephan Albers, July Antonicheva

Version: 1.7-SNAPSHOT

4 Developer's Guide

4.1 Overview

This section gives you a brief overview about the structure of GrailsFlow and how the system works internally.

The most important components are:

  • Process definitions
  • Processes
  • Process variables
  • Worklist management
  • Actions
  • Documents

Process Definitions

The workflow engine consists of process definitions and information about running processes. They define the basic steps of a process. We call these steps nodes.

Process Definitions are stored in Groovy classes using the builder syntax. You find examples in web-app/WEB-INF/workarea/processes/ in your GrailsFlow directory.

When editing processes, this information gets parsed and stored into ProcessDef and ProcessDefNodes. After the editing process GrailsFlow creates new Groovy files from these definitions.

The node types are:

  • activity node - the system executes the Groovy code stored in the node ;
  • fork node - engine allows splits for the parallel execution of multiple steps. Split nodes mark the beginning of

a parallel execution

  • join node - the counterpart to split nodes, where multiple parallel executions are joined together
  • wait node - wait nodes expect user input. When the process execution reaches a wait node, the node becomes

visible in the worklist. Typically the user enters or changes values of process variables or makes decisions about where to go to next (decides for/specifies an event).

We move from node to node via so called "events" (also called transitions). When a node gets executed it returns a value (name of the event). Based on this value the workflow engine moves to the next node that is connected via this event.

Processes

Running Processes as well as historical information is stored in:

  • Process and
  • ProcessNode

Whenever a process enters a new node, a process node record is created in the database. After the process leaves the nodes, the record gets updated with status information.

This means ProcessNode includes all historical information about the process. When the process enters the same node twice, there are two records for this node.

This makes it easy to see the status of running or old processes by looking at the process and processNodes tables.

Start process: Extendable Process Starting

There is a possibility to start Process/send Event with the help of external groovy scripts located in src/samples/callbacks/. These scripts are called on /process/executeCallback/<scriptFileName> URL request. For example, request of /process/executeCallback/sendEvent will invoke execution of callbacks/sendEvent.groovy script.

Request, params and result are passed to callback script. Callback script should parse request to get processKey, nodeID, event, process variables and to store them in the result parameter.

For example:

  • result.processKey = params.processID
  • result.nodeID = params.nodeID
  • result.variables = productID: params.productId, catalogID: params.catalogID

Based on the result of callback script execution, ProcessController would update process variables and invoke event of appropriate node.

Parameters that are available in the callback script:

  • request - HttpServletRequest instance,
  • params - String valued parameters passed to the ProcessController by request,
  • result - object for storing parsed process parameters.

Script should fill following result properties:

  • result.processKey - key of the process that should be executed,
  • result.nodeID - name of the node that should be executed,
  • result.event - event that should be send to the node,
  • result.requester (optional, default is logged user) - ID of the person that invokes event,
  • result.variables (optional) - map of name->value pairs of process variables that should be updated,
  • result.message (optional, default is "Callback received") - message to be send in response in case of successful execution,
  • result.statusCode (optional, default is 200) - HTTP status code to be send in response.

Process Variables

Process Variables are all public variables declared in the process. The variables are read via reflection and stored in the ProcessVariable class as a map. Whenever the variables have changed, they get stored back into the database.

Therefore, process variables are always persisted in the database, so that the process can go on, even after the application has been restarted. However, process variables do not include any historic information, only the current values. This is different from nodes, where we see the complete history in process nodes.

Worklist management

GrailsFlow supports automatic execution of nodes and manual user input. Workflows often need the user to make decisions or give input to the process.

GrailsFlow has functions for worklist management integrated. Whenever a process designer specifies a wait node, the execution of the process stops when reaching that node. The node becomes visible in the worklist of the user that is currently assigned to that process node.

When a user clicks on the node there can be three possible interactions:

  • automatic forms - user sees all process variables the process designer has given read access to and can enter

values for variables. GrailsFlow also shows buttons for all events/transitions that leave the node, so the user can decide which transition to take by clicking on the button.

  • manual form - GrailsFlow allows the upload of forms that have been defined manually. These forms are shown instead

of the automatically generated form.

  • manual forms and controller - This allows process designers to define their own forms management and controllers.

This is the most flexible way of interacting with the user, however, it is also takes the biggest effort. Controller can deal with process variables or other Grails components as well as span multiple pages using the Grails WebFlow functionality.

Actions

Grailsflow provides several pre-defined actions out of the box, eg. SendMail, Log, Evaluate. To define a new Action, reate a new class that implements the Action interface and put it into "srcgroovycomjcataloggrailsflowactions".

GrailsFlow automatically collects the available actions and presents them in the action editor.

Actions can have parameters. These are defined as simple instance variables within the Action class. The action editor automatically creates a user interface for the parameters and ask the user for the values.

The user can select three different value type for the parameter of an action: process variables, constant values or arbitrary Groovy expressions.

At runtime, a new action is created, the values of the parameters or constants are assigned to the instance variables of the action and the "execute" methods are called.

Documents

Documents are a special type of process variable that allows user's to store arbitray files in a process. You declare a process variable in your process class through the Process Editor (or manually) with the type Documents. When the variables can be entered, GrailsFlow presents an upload button for documents. The file is uploaded and stored in a directory. We create one directory for each day. The file name is cleaned up and extended by the process id and a counter. The link to the file is stored in the DB.

The file can be downloaded and a new version can be uploaded, if the variable can be changed in a later step (node). The new version is stored the same way, so GrailsFlow provides a full version history for documents.

4.2 Changing a Node Owner dynamically

Grailsflow allows user's to define assignees or owners of node. An assignee or node owner could be a specific user, a list of roles or groups. These are the persons that see the workitems, work on them and move them to the next status.

The owners of a node are pre-defined in the process editor, however they can be overwritten within the workflow. When a worklist is shown, the system checks for all nodes that have the same user id, roles or groups in the assignee list that the current user has and shows those nodes in the worklist.

There are processes that require that the assignee of a node is determined/calculated dynamically, e.g some of them are visible only for an administrator, some are available for simple users, others for restricted groups, etc.

GrailsFlow supports this by providing a system variable nextOwners that allows them to set the owners for the next nodes. This variable is not stored in the DB, but evaluated while executing a node and lives only in the action execution context.

When the actions of a node are finished, that next nodes are created and GrailsFlow writes "nextOwners" as the "owners" into those nodes. In the definition of actions, they look and act like simple process variables.

The difference between 'nextOwners' and 'nextOwner' properties is that in 'nextOwners' , you can specify different assignees for specific nodes, while 'nextOwner' allows you to specify assignees for all following nodes. This is more convenient, if you have only one following node - in this case you do not need to specify 'nodeID' for that node.

The user names/roles/groups in assignees should have the same types/values, as the information coming from the AbstractWorklistProvider implementation. This is typically "USER_username", "ROLE_rolename" and "GROUP_groupname".

Example: if you want to create restrictions for user roles and your user class contains many roles, then you will have something like this in your process description:

In Process Definition:

…
    action {
      …
      def nextAssignee = Evaluate(expression: '["ROLE_USER", "ROLE_ADMIN"]')

}

4.3 Creating Actions

Actions

Actions are the principal unit that execute all activities and operations in GrailsFlow. They are written in plain Groovy and stored in a specified folder. The folder can be customized in Spring configuration (like paths to processes scripts and document attachments), by default it is configured to the 'actions' folder in the workarea. When the node is executing, the necessary action script is loaded as a file, dynamically parsed to Java class and then executed (method execute() of the class is called).

When you write a new action, you have to subclass the abstract class Action. All actions can then be used in the action editor and the 'action' closure of the process definition.

How to implement a custom action

The class Action provides several methods that are further described here.

  1. Execute() method

The abstract Object execute() method need to be implemented. This methods "is" the actual action/does the operation that the action does. The result that is returned from this method is used as the action result and can be assigned to variable value or returned as the result of 'return' statement.

  1. Helper method getObjectByName(String beanName)

Allows to use preconfigured Spring beans in the context of an action. The beans are look up in the Spring context configuration and if nothing is found - null result is returned. Can be used for arbitray operations eg. executing some DB operation, integrating external beans etc.

  1. Map actionInfo

actionInfo contains basic information about the current process and execution context that can be used in the action implementation. It provides the

  • process key,
  • current node ID,
  • locale,
  • siteBase,
  • requester.

Grailsflow provides a number of basic actions that are useful in many workflows. Among them are:

  • LogAction - writes some log information in the trace or the log file.
  • EvaluateAction - evaluates arbitrary Groovy expressions. This is very powerful and generic and reduces the number of

actions that you need to write.

  • SendMailAction - sends mail to the give recipient

4.4 Extendable Start Process

In Grailsflow it is possible to start process in two ways:
  • start process with defaults values by passing process name;
  • start process from external system (with requested variables values).

In the first, you can start process with default values (pre-defined in process definition script). When you start the process and your first node is manual (the node of type 'Wait') you will be automatically redirected to the manual form and the process will be started only when you submit it (change/set values for the process variables).

But sometimes there is a situation when the external system wants to start the process. The system should provide the starting data for process (the values for process variables). For example, the several different systems use the same process definition 'CreateProduct' for creating product. For starting the process, the system should provide 'productId' and 'catalogId' values. The problem is that systems can have (and usually have) different formats for sending parameters, e.g. in XML. It means that for each and every system, a special 'parser' script is needed for parameters, and then when Grailsflow gets the parameters in usual form it can start the process using standard procedure.

It is possible to start a process from an external system using Grailsflow 'Extendable StartProcess' feature. When you want to use it you need:

  1. Have a Groovy 'callback' script in application workarea (the application contains Grailsflow plugin installed). This

script should be places in workarea directory according to 'callbacksPath' bean value. The bean is defined in Grailsflow, and can be redefined in application, but the defauls value is "callbacks" folder. Callback script should parse request to get process variables (and possibly processType and user) to store them in the result parameter. Parameters that are available in the callback script:

  • request - HttpServletRequest instance
  • params - String valued parameters passed to the ProcessController by request
  • result - object for storing parsed process parameters

Parameters that should be provided in the 'result' map (in callback script):

  • variables - (optional) - map of name:value pairs of process variables that should be used.
  • requester - (optional, default is logged user) - ID of person that invokes event
  • processType - (required) - the name of process definition that should be started
  1. Use URI for starting process:

process/extendedStartProcess/<id>

where 'id' - is a Groovy callback script name.

Lets check the example: your system provides the parameters for starting in the following view (as XML):

<?xml version='1.0' encoding='UTF-8'?>
  <test> 
    <DOCUMENT>   
      <PRODUCTID>11240_Printer</PRODUCTID>
      <CATALOGID>HP</CATALOGID>   
      <MANUFACTURER>JC_MANUFACTURER</MANUFACTURER>
      <STATUS>UPDATED</STATUS> 
    </DOCUMENT>
  </test>

You create script 'TestScript.groovy' that knows how to get parameters from request:

// Parsing request body

def doc = request.XML if(!doc || !doc.DOCUMENT) { result.message = "Cannot parse request" result.statusCode = 500 return } // Getting process parameters def productId = doc.DOCUMENT[0].PRODUCTID[0] def catalogId = doc.DOCUMENT[0].CATALOGID[0] def manufacturer = doc.DOCUMENT[0].MANUFACTURER[0] ef SAP_Status = doc.DOCUMENT[0].STATUS[0]

def variables = [productId: productId, catalogId: catalogId, manufacturer: manufacturer, SAP_Status: SAP_Status]

def securityHelper = applicationContext.getBean("securityHelper") if (!securityHelper) { result.message = "Security Helper bean is not configured." result.statusCode = 500 return }

result.processType = "TestProduct" result.requester = securityHelper.getUser(null) result.variables = variables

Then you place script 'TestScript' in workarea (in callbacksPath folder) and start the process by passing URL:

<your_application_context_path>/process/extendedStartProcess/TestScript

For example:

http://localhost:8080/pim-grails/process/extendedStartProcess/TestScript

4.5 Generating forms from Variable definitions

Types of Forms

Grailsflow supports automatic execution of nodes and manual user input. Workflows often need the user to make decisions or give input to the process.

GrailsFlow has functions for worklist management integrated. Whenever a process designer specifies a wait node, the execution of the process stops when reaching that node. The node becomes visible in the worklist of the user that is currently assigned to that process node.

When he clicks on the node there are three possible interactions:

  • automatic form: the user sees all process variables that the process designer has given read access to. He can enter

values for variables. GrailsFlow also shows buttons for all events/transitions that leave the node, so the user can decide which transition to take by clicking on the button.

  • manual form: GrailsFlow allows to upload forms that have been defined manually. These forms are shown instead of the

automatically generated form.

  • manual forms and controller: This allows process designers to define their own forms management and their own

controllers. This is the most flexible way of interacting with the user; however it is also the biggest effort. Controller can deal with process variables or other Grails components and can also span multiple pages using the Grails WebFlow functionality.

Create a Form

In the Process Editor under Process Nodes, click the "Add" button to create a node.

Enter Node ID and select type as 'Wait' to display the visibility section. Define the visibility for each of the variables (INVISIBLE, READ_ONLY, REQUIRED and WRITE_READ). Select form generation as - "Use Automatic Form" and click the "Save" button.

The user interface form is auto generated as below.

The user interface form is auto generated as below.

To create a manual form, select Use Custom Form, a text box appears for entering html code for the form. Enter the manual form code and click button 'Generate'

To create a manual form with a controller select the option Use Custom Controller and Form and enter the code for Manual form and controller and click button 'Generate'

If the editor is an external one, select Use External Editor option and enter the URL for the same.

Example:

  • Views Section
    • size
    • widget
    • rows
    • cols
    • styleClass
    • template
    • displayKey
    • remoteFields
    • dateFormat
    • restriction
  • Descriptions Section
    • description_(en|de|fr|...)
    • label_(en|de|fr|...)
  • Constraints Section
    • required

Example:

class AutoFormsGenerationTestProcess {
    public String  productId
    public Link userSite
    public Address address
    public String commentary

static views = { // the order of fields is taken into account when form auto generates, // if the property is not specified in this section it will be displayed // below specified properties

address(widget: "externalSearch", template: "search/address.gsp", displayKey: "addressID", remoteFields: "name1,city") commentary(widget: "textarea") productId(size:35) }

static descriptions = { AutoFormsGenerationTest(description_en: "Process workflow shows automatic forms generation.", description_de: "Process workflow shows automatic forms generation.") address(description_en: "You can specify address here.", description_de: "You can specify address here.", label_en: "Address", label_de: "Address") productId(label_en: "Product ID", label_de: "Product ID") }

static constraints = { productId(required:true) address(required:true) }

Grailsflow Auto Forms Generation

size

Usage: Uses a value to restrict the size of property input field Example:

productID(size: 35)

widget

Usage: specifies the widget that represents this process variable property, Possible values:

  1. textarea
  2. externalSearch
  3. list

Example:

commentary(widget: "textarea")
address(widget: "externalSearch")

status(widget: "list")

External Search: If you want to use widget 'externalSearch', it is also important to specify the 'displayKey' and 'template' attributes, because they specify the template and bean property for rendering.

The mechanism of external search rendering is as follows: property is rendered as readonly input with 'search' and 'clear' link-icons. By clicking the 'search' link - you see the pop-up window with a 'template' page. It can be the page with the search form or it can contain the list of all available values for the property and the user has the option to possibily select one. Then the inputs of the property are pre-selected with these values. The requirements for the template page are:

  • The template before closing should call opener function, the name of this function is available in the template as

the 'callbackFunctionName' parameter. Also in the template, there are some more available parameters, they are: ID0 - the identifier property of the preselected value. fieldName0 - the property name (process variable name).

  • It is possible to rewrite and create your own function that fills the opener inputs with selected value. But in this

case you need to know how the widget Id's are formed.

  • Pay attention how the opener inputs can be filled: if you want to call opener function, you should transmit some

params with the function since this is the: 'key' - the identifier property of your selected values and any other parameters that have the same names as properties of variable (specified as displayKey and remoteFields properties names), for example: key = 3 - the unique identifier of Address object. addressId = 'MIRAX GROUP' - value of Address display Key property city = 'London' - the value of city property street= 'Piccadilly' - the value of street property If you want to fill owner fields with you own JavaScript function, please pay attention how the opener widgits Id's are formed. The key property with Id '${propertyName}' for opener window, the remoteFields have the following ID structure:

${propertyName}_${remoteField}, for example:

opener.document.getElementById("address").value = 3
    opener.document.getElementById("address_addressId").value = addressID
    opener.document.getElementById("address_city").value = city
    opener.document.getElementById("address_street").value = street

List: If you want to use widget 'list', you can also specify 'displayKey' and 'restriction' attributes.

rows

Usage: defines the number of rows for textarea widget Example:

commentary(rows: 10)

cols

Usage: defines the number of columns for textarea widget Example:

commentary(cols: 50)

styleClass

Usage: defines which style class should be applied for widget Example:

productId(styleClass: "productEntry")

template

Usage: this property is important for externalSearch, it describes path to template that will use for external search Example:

address(template: "search/address.gsp")

displayKey

Usage: this property is important for externalSearch and list widgets, if it's specified then selected value is represented by this property of bean. Example:

address(displayKey: "addressId")

remoteFields

Usage: this property is important for externalSearch, the properties names are comma-separated, specifies additional fields which are displayed on UI Example:

address(remoteFields: "city, street")

dateFormat

Usage: can be useful for properties of type Date, there we can specify the pattern for date, e.g. 'y-m-d H:i:s' Example:

startDate(dateFormat: "y-m-d H:i:s")

restriction

Usage: can be useful if you specify widget list. In this case in restriction input you can write restrictions for list values. Example:

address(widget: "list", restriction: "address.city = 'Berlin'")

It means that in list should be shown only addresses with property City ='Berlin'

description

Usage: specifies the description for process or process variable property Example:

commentary(description_en: "Put your commentary here.", description_de: "Put your commentary here.")

label

Usage: specifies the label for property or node event Example:

catalogId(label_en: "Product Catalog ID", lable_de: "Product Catalog ID")

Set(label_en: "Set Values", label_de: "Set Values")

required

Usage: set to true if the property value is required Example:

productId(required:true)

4.6 Manual Forms

Why do we use Manual Forms

Sometimes it’s not convenient to use Grailsflow automatic forms for process flow. They have a number of restrictions.

All automatic forms have the same layout: the list of variables and the list of available events. In Grailsflow, there is a possibility to create manual or custom forms. On your manual form you can use any styles, scripts, functionality. See example.

Automatic form for HolidayRequest process definitions:

Custom form for the same process definition:

Based on Grails: Groovy + GSPs

The manual form creation if based on Groovy and GSP concepts - The manual (or custom) form is a GSP template that will be rendered on the ‘Node Details’ page. When you decide that you need to create a form, the following links about Grails, Groovy and GSP (groovy server pages) can be useful for you:

How to create Manual Forms and integrate into Process definition

When you decide that you need a manual instead of an automatic form for your node, there are two ways to do it:

  • Create simple Manual form;
  • Create Manual form with Custom Controller

The information for the second (Create Manual form with Custom Controller) option can be found in chapter 11. This feature works only in development mode.

When you finish with the form and press the ‘Generate’ button, the GSP template is generated and stored in the application views. When the node is activated, the manual form will be rendered to the user. Each form belongs to the process node and stored in views as - path‘views/manualForms/<ProcessType>/_<ProcessNode>.gsp’. The Manual form is rendered as the part of the 'Node Details' page. Only server messages (standard error messages) can be added to the page automatically (at the top of the page). If your form cannot be rendered, the error message will be displayed.

Access of Process Variables

On the manual form you can access all process variables. They are in bean ‘nodeDetails’ that is available on the manual form. You can get them by using the following signature:

${nodeDetails.variables}

Where 'variables' is a Map that contains entries variableName: variableDetails VariableDetails bean contains the following list of properties:

  • name String variable name
  • typeName (changed to type since version 1.0) type of variable
  • label Map of languageID -> variable label
  • description Map of languageID -> variable description
  • value Object variable value
  • visibility Variable visibility
  • required Is variable required or not
  • view View of variable (See more information in chapter 8)

For example, you can access property 'value' of variable using the following notation: ${nodeDetails.variables.requesterName.value}

If you want your variable to be updated after form submitting you need to follow the rule that variable should have property 'name' specified like:

name="var_<variableName>".

Available Events (transitions)

Also on the manual forms you can get information about all available transitions from current node. They are stored in bean ‘nodeDetails’ and can be accessed using signature:

${nodeDetails.events}

Where "events" is a set of objects. Each object contains the next information about transition:

  • event;
  • label (the Map of entries: languageID -> event label) - it is used for making events translatable.

If you want to use transition on UI you need to take into account the following rule: your HTML submit component should have property ‘name’ specified like name = “event_<event>” .

See example:

Pay attention, that if you want to submit any information to the server you need to place it in <g:form> tag. For more information check http://www.grails.org/Tag+-+form.

Available Beans and Services

There is a bean "nodeDetails" that is available on manual form. This bean is of type com.jcatalog.grailsflow.engine.NodeDetails and contains the following info:

NodeDetails has following properties:

  • process Object contains information about process
  • nodeID String node ID
  • label Map of languageID -> node label
  • description Map of languageID -> node description
  • caller String node caller
  • status FlowStatus node status
  • startedOn Date node startedOn
  • dueOn Date node dueOn
  • assignees Set<String> of assignee IDs
  • events Set<EventDetails> of events
  • variables Map of variableName -> variableDetails

How to access DB entries

On the custom form it is possible to access database instances using standard Grails possibilities (working with domains) and Groovy tags:

<g:select from="${com.jcatalog.core.Country.list()}"
          optionKey="countryId"
          optionValue="countryId"
          value="${orderAddress?.country}"/>

This example shows how to create selectbox with Countries (countryID are shown).

If you need to access the DB table, you need to specify the domain class (full class name) and then call one of the dynamic methods that Grails provides for domains.

For example, you can get all entries from table Product:

com.jcatalog.product.Product.list()

or find products using some filters:

com.jcatalog.product.Product.findAllByProductIdLike(“%Printer%”)

com.jcatalog.product.Product.findWhere(productId:“Printer”, catalogId: “HP”)

For more information about available dynamic methods, see http://grails.org/DomainClass+Dynamic+Methods .

Custom tags provided by Grailsflow

For the automatic form you can specify ‘view’ and default values for each process variable. For the manual form you can use all the same ‘views’ or tags. Among them:

  • SimpleView
  • TextAreaView
  • SelectBoxView
  • CheckBoxView
  • DateView
  • LinkView
  • DocumentView
  • ListObjectsView
  • ExternalSearchObjectView
  • DefaultView

TThe 'view' property can be specified for each process variable in the variable editor. Then, if you specify the view, you can ask Grailsflow to use this 'view' for rendering the variable. For this reason, you can use 'variableInput' Grailsflow tag (implemented in developing version):

<gf:variableInput variable="${variable}"/>

If you do not specify 'view' templates for each process variable you can use view templates directly on the manual page:

<g:customizingTemplate
 template="${ com.jcatalog.grailsflow.model.view.CheckBoxView.template}”
 model="[variable: variable, view: variable.view, parameterName: parameterName]"/>

In the tag there are the following parameters:

  • template (that is the template that should be used for variable rendering);
  • model (the information that will be available on the template.)

The template requires ‘variable’ for rendering, ‘view’, and ‘parameterName’ – it provides to the server the name of the variable for updating DB entries after submitting.)

Please see the example how to get ExternalSearchObjectView for variable.

You can use the following notations:

<gf:variableInput variable="${variable}"/>

Use this notation when variable 'view' and all necessary parameters for displaying external search (like displayKey, URL for search) are specified in process script. Also it's possible to get external search component using tag 'customizingTemplate':

<g:customizingTemplate
 template="${com.jcatalog.grailsflow.model.view.ExternalSearchObjectView.template}”
 model="[variable: variable, view: variable.view, parameterName: parameterName]"/>

In that case, all parameters that are necessary for ExternalSearchObjectView (e.g. displayKey, URL for external search) will be taken from ‘variable.view’ object. But pay attention in that case you need to specify all these attributes for the variable in Process Variable Editor.

If you did not configure 'View' for the process variable definition, it is possible to use External search too. You can do it using Grails <g:render> tag. For ExternalSearchObjectView there are three parameters that you can specify:

  • displayKey (property for display in input field);
  • searchUrl (is necessary, URL for external search page);
  • additionalFields (Additional fields for displaying, they are usually displayed as readonly fields.);

The following example should show external search component for Address object.

<g:render contextPath="${pluginContextPath}"
 template="/variableViewTempaltes/externalSearchObjectView"
 model="[variable:nodeDetails.variables.address,
 view:new com.jcatalog.grailsflow.model.view.ExternalSearchObjectView(displayKey:'addressID',
 searchUrl: 'search/address.gsp',
 additionalFields: 'name1, city'),
 parameterName: 'externalSearchObjectView_address']"/>

Where:

  • contextPath Path for template searching
  • template Template for rendering
  • model Information that should be available for the template

In the 'model' you put 'variable' object. As you do not have a view specified for the variable, we can create it:

new com.jcatalog.grailsflow.model.view.ExternalSearchObjectView(…)

In the brackets, it is possible to define properties for this 'view'.

new com.jcatalog.grailsflow.model.view.ExternalSearchObjectView(displayKey: 'addressID', searchUrl: 'search/address.gsp', additionalFields: 'name1, city')

As mentioned before, the events and variables have Maps of labels and descriptions (only variables have descriptions). You can show the labels and descriptions according to the requested locale using Grailsflow tag 'translatedValue'’.

For example:

<g:translatedValue translations="${variable.label}" default="${variable.name}" />

or

<g:translatedValue translations="${variable.description}" default="" />

How to include user owned GSP templates

It is possible to include your own prepared templates into the manual form.You can do it using Grailsflow tag “customizingTemplate”.

<g:customizingTemplate template="/myForms/eventForwarding" model="[currentAssignees: nodeDetails.assignees]"/>

Customize Return Controller and Action

By default, after the manual step form execution, you will be forwarded to the Process Details page. You can customize this behavior by specifying the return controller and action in your manual page. For example you can set it to 'showWorklist' - this means that after form submission, you will be redirected to the Worklist page. Please see the following example:

<input type="hidden" name="returnController" value="process"/ >
<input type="hidden" name="returnAction" value="showWorklist"/ >

Releases of Grailsflow-1.0 and higher use parameters 'resultController', 'resultAction' instead of 'returnController', 'returnAction'.

Multi-Step pages

This feature works only in the development mode of Grails. It makes it possible to enhance process flow (add some additional functionality and steps) without changing process definition. This can be done by using multi-step page. For this, you need to select ‘Use Custom Controller and Form’ in Node Editor. In that case your ‘manual form’ content (entered in the textarea) will be rendered as the first step. It should contain links/buttons that leads to the actions in your Custom Controller. Your Custom Controller can make some actions and then render the next ‘step page’ (page can be created using the ‘Add Multi-Step Page’ link). This step can lead to the Custom Controller and the Controller will render the next step or redirect to the Grailsflow controller and continue the flow.

Multi-Step page belongs to the node and stored in the views under path, see some screenshots:

Add one more Step:

Multi-Step page belongs to node and stored in the views under path:

‘views/<ProcessType><ProcessNode>/<StepName>.gsp’,

Pay attention that the name for the controller is generated by Grailsflow and it has view:

<ProcessID_NodeID>Controller.groovy

In the example above you can see that the form has “controller = ‘test_SetValues’”. Where ‘Test’ – is a name of Process, and ‘SetValues’ is a name of node for which you prepare the manual form.

Pay attention that manual (or custom) form is included in GSP page as template. This template is included in

<g:form action="sendEvent" controller="process" ...> tag, it means that you should not use more forms in your manual page. If you need to submit other form action you can use JavaScript features to change form action and controller. For example:

<input type="button" onclick=" this.form.action = ...; form.submit(); " />

4.7 Manual Forms Actions

Manual Forms Actions

From version Grailsflow-1.2.7 it is recommended to have manual nodes without actions (it means without closure action at all).

We assume that manual node is the form (automatic or manual) and it should not contain any Groovy code for execution. The Wait node is only used to prepare user form, get new values for process variables and define next transition. If you need any kind of validation for process variables or some other code, please define it in the next 'Activity' node.

But these changes should be compatible with the previous versions of Grailsflow (where manual nodes can have code). It means that in case if 'Wait' node has action{} closure, the engine will execute it, but write warning, for example 'WARNING! The action{} closure is deprecated in manual node definition! Such code may lead to system dead locks.'

Also, there is a notification about manual forms actions in Process Editor (Action Editor):

4.8 PostKillOperation Handler

PostKillProcessHandler

There is a possibility to define some necessary activity in case of process killing (e.g. close connections, send emails, etc.). In that case you can implement class with interface PostKillProcessHandler (com.jcatalog.grailsflow.process.PostKillProcessHandler) and write your code in its method handle and then define bean postKillProcessHandler in application Spring context.

By default this bean is not configured - so there is no activity in case of process killing. In case if bean postKillProcessHandler is defined, the postKillProcessHandler should be called after process interruption but before saving KILLED status for process and nodes. In case if process cannot be killed successfully (e.g. status 'KILLING') no activity is called.

4.9 Customize Due Dates calculation

Customize Due Date calculation

It is a possible to customize Due Date calculation for Process Nodes and configure calculation behaviour for all processes or for certain Process Type. By default, the process node due date calculated as:

node.dueOn = node.startedOn + nodeDef.dueDate

where

  • node.startedOn - Date when node was ACTIVATED.
  • nodeDef.dueDate - Long value from process definition.

Sometimes, it is necessary to change behaviour - e.g. if the dueDate (node.,dueOn) is a weekend, it will need to be shifted. Use the Config.groovy file of your application to configure Due Date calculation.

grailsflow.customizedDueDate = { Date dueDate ->
     Calendar date = Calendar.getInstance()
     date.setTime(dueDate)
     if (date.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
         date.add(Calendar.DATE, 2)
         return date.time
     } else if (date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
         date.add(Calendar.DATE, 1)
         return date.time
     } else return date.time
}

Or also it is possible to do for certain Process Type:

grailsflow.HolidayRequest.customizedDueDate = { Date dueDate ->
     Calendar date = Calendar.getInstance()
     date.setTime(dueDate)
     if (date.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
         date.add(Calendar.DATE, 2)
         return date.time
     } else if (date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
         date.add(Calendar.DATE, 1)
         return date.time
     } else return date.time
 }

4.10 How to get Process Management Results in JSON

How to get Process Management Results in JSON

Sometimes the developers don't want to get Grailsflow core pages with results of process management (e.g. process details, or search results), but need information for their own pages. So, it is possible to get Process Management Results in JSON format. For this reason simply put additional parameter format=JSON in your request. For more details about implementation, please check Grails closure withFormat

The mechanism is working for process management actions such as

  • 'showTypes'
  • 'search'
  • 'showWorklist'
  • 'showProcessDetails'
  • 'showNodeDetails'
  • 'startProcess'
  • 'killProcess'
  • 'sendEvent'

For example,

http://localhost:8080/grailsflow-demo/process/showWorklist?format=json

or

http://localhost:8080/grailsflow-demo/process/search?format=json

4.11 Support of clustering

Support of Clustering

There are situations when it is necessary to have a kind of load balancer. For example, when the user have several installation of Grailsflow on the one server or when we need to work in cluster mode.

Grailsflow plugin has a kind of load balancer. The idea is based on the following aspects: every automatically executed node creates lock (entry in GrailsflowLock table) for process before execution. After node execution the lock is removed. Locks are stored in GrailsflowLock table. Also there is a table ClusterInfo, it contains information about clusters. In Grailsflow s*- There is a ClusterCheckerJob that fires according to configured values ( grailsflow.scheduler.clusterChecker.repeatInterval value). The job updates ClusterInfo entry about the cluster in which it runs and then checks information about other clusters - if they are expired, e.g. they didn't update ClusterInfo entry for more then (grailsflow.scheduler.clusterChecker.repeatInterval x 2) time , then we assume that the cluster is not runnable. If the cluster is desided to be stopped, we remove all its locks from GrailsflowLock and delete its ClusterInfo entry. Also the ClusterCheckerJob checks the locks for expired time. If the locks are inactive more then grailsflow.clusterChecker.lockExpiredInterval time, it means that the process is not running or probably the node cannot be executed or process is not responding. In that case the process should be killed and it lock is deleted.

Properties to configure:

  1. grailsflow.clusterName = "Some_cluster_name"

By default, it is configured in plugin descriptor by the following way:

grailsflow.clusterName = "gfw_${new Date().time}_${new Random().nextInt(1000000)}"
  1. grailsflow.clusterChecker.lockExpiredInterval = "2000000"

This is a long value, value interval when the locks are expired and should be deleted and processes are killed. The default value is 6000000 ms.

  1. Configuration for ClusterCheckerJob:

grailsflow.scheduler.clusterChecker.autoStart = false // by default its 'true'
    grailsflow.scheduler.clusterChecker.startDelay = 10000  // 30 sec by default
    grailsflow.scheduler.clusterChecker.repeatInterval = 50000 // 1 minute by default