Jbpm Plugin

  • Tags: workflow
  • Latest: 0.1
  • Last Updated: 11 August 2008
  • Grails version: *
  • Authors: null
0 vote
Dependency:
compile ":jbpm:0.1"

 Documentation

Summary

Description

Jbpm is an open source workflow / BPM solution. It helps coordinate business processes involving multiple people or services. The goal of this plugin is to make it easier to use Jbpm in a grails application

Features

  • Easy integration with jbpm (handles all the configuration changes required)
  • Manage processes - deploy, update process definitions
  • Worklist application to quickly prototype new business processes

Usage

The plugin can be used in two modes. The basic mode provides the integration support and support for automatically deploying process definitions. It also makes the jbpmTemplate (provided by spring modules) as a spring bean that can be used from within a grails application (Controller / Service etc) to interact with the jbpm.

On top of this the plugin also provides a worklist application.

Basic Mode

Create a grails application called "Expense" and install the jbpm plugin

grails install-plugin jbpm

The process definitions need to placed under

grails-app/conf/jbpm/processes
. Each process definition needs to be created under a separate folder and file name should be processdefinition.xml (this is the format used by the eclipe jpdl plugin) Create a folder called "Expense Reimbursement" and add the following contents to a file named processdefinition.xml under that folder.

Download

<?xml version="1.0" encoding="UTF-8"?>
<process-definition  xmlns=""  name="Expense Reimbursement">
    <swimlane name="ROLE_EMPLOYEE"><assignment actor-id="employee"/></swimlane>
    <swimlane name="ROLE_MANAGER"><assignment actor-id="manager"/></swimlane>
    <swimlane name="ROLE_ACCOUNTANT"><assignment actor-id="accountant"/></swimlane>

<start-state name="New Expense Claim"> <task swimlane="ROLE_EMPLOYEE"/> <transition to="Manager Review" name="Submit"/> <transition to="Draft" name="Save As Draft"></transition> </start-state>

<task-node name="Manager Review"> <task swimlane="ROLE_MANAGER"/> <transition to="Settle Claims" name="Approve"/> <transition to="Amend Claim" name="Amend"/> <transition to="Rejected" name="Reject"/> </task-node>

<task-node name="Amend Claim"> <task swimlane="ROLE_EMPLOYEE"/> <transition to="Manager Review" name="Re Submit"/> </task-node>

<task-node name="Settle Claims"> <task swimlane="ROLE_ACCOUNTANT"/> <transition to="Settled" name="Pay"/> <transition to="Rejected" name="Reject"/> </task-node>

<task-node name="Draft"> <task swimlane="ROLE_EMPLOYEE"/> <transition to="Manager Review" name="Submit"/> <transition to="Deleted" name="Delete"/> </task-node>

<end-state name="Settled"/> <end-state name="Rejected"/> <end-state name="Deleted"/> </process-definition>

Run the app and navigate to, you should the "Expense Reimbursement" process listed.

http://localhost:8080/Expense/process/list

You can then use the jbpmTemplate to hook it up with the rest of the grails app. For example the following code will create new instance of the process

class MyController {
    def jbpmTemplate

def instantiate = { jbpmTemplate.execute() { ctx -> def processInstance = ctx.newProcessInstanceForUpdate("Expense Reimbursement") // write code here to associate process variables

processInstance.signal() } } }

The jbpmTemplate's execute method takes a closure as input. The closure is invoked with one argument - the JbpmContext. The cotext can be used to perform various operations (deploy process definitions, create a new instance of a process, find pending tasks assigned to an user etc)

Using the Worklist Application

The plugin also provides a worklist application that can be used to quickly prototype the business process. To make use of the worklist application you will need to use the Acegi plugin for authentication and adhere to the following conventions.

  • The actor-id used in the jbpm process definition should be same as the user name
  • The swmilane name should be same as the role name
  • Create GSP templates for the list, detail view and form for instatiating a process
The following instructions will guide you through the setup required to use the worklist application.

Install the acegi plugin.

grails install-plugin acegi

and then create the auth domain classes using.

grails create-auth-domains

Add the following code to the BootStrap for setting up the users & Roles. We are setting up three roles (ROLE_EMPLOYEE, ROLE_MANAGER and ROLE_ACCOUNTANT) and one user per role (employee, manager and accountant). Please note that these should match with the swimlane and the actor-id specified in the processdefinition.

Donwload

class BootStrap {
     def authenticateService
     def init = { servletContext ->
     	createDefaultRoles()
     	createDefaultUsers()
        createRequestMap()
     }

private def createDefaultRoles() { if (!Authority.findByAuthority('ROLE_EMPLOYEE')) new Authority(authority:'ROLE_EMPLOYEE',description:"Employee").save()

if (!Authority.findByAuthority('ROLE_MANAGER')) new Authority(authority:'ROLE_MANAGER',description:"Manager").save()

if (!Authority.findByAuthority('ROLE_ACCOUNTANT')) new Authority(authority:'ROLE_ACCOUNTANT',description:"Accountant").save() }

private def createDefaultUsers() { def employee = Person.findByUsername('employee') if (!employee) { employee = new Person(username:'employee',userRealName:'Employee', passwd : authenticateService.passwordEncoder('welcome'), email:'employee@lxisoft.com',enabled:true) employee.save() }

def manager = Person.findByUsername('manager') if (manager == null) { manager = new Person(username:'manager',userRealName:'Manager', passwd : authenticateService.passwordEncoder('welcome'), email:'manager@lxisoft.com',enabled:true) manager.save() }

def accountant = Person.findByUsername('accountant') if (accountant == null) { accountant = new Person(username:'accountant',userRealName:'Accountant', passwd : authenticateService.passwordEncoder('welcome'), email:'accountant@lxisoft.com',enabled:true) accountant.save() }

Authority.findByAuthority('ROLE_EMPLOYEE').addToPeople(employee) Authority.findByAuthority('ROLE_MANAGER').addToPeople(manager) Authority.findByAuthority('ROLE_ACCOUNTANT').addToPeople(accountant) }

private def createRequestMap() { new Requestmap(url: '/login/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save() new Requestmap(url: '/task/**', configAttribute: 'IS_AUTHENTICATED_FULLY').save() new Requestmap(url: '/process/**', configAttribute: 'IS_AUTHENTICATED_FULLY').save() new Requestmap(url: '/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save() }

def destroy = { } }

Create a domain class "ExpenseClaim" for capturing the details of the expense claim.

Download

class ExpenseClaim {
    def String purpose
    def Date date
    def double amount
}

We now need to create a form to enable an employee to submit a new expense claim and kick start the process. For doing that we need to create a GSP template. Create the file "_New Expense Claim_create.gsp" (the name is derived from the name given to the start-state in the process definition) under the folder "grails-app/views/jbpm/templates/Expense Reimbursement" with the following contents

Download

<table>
    <tr class="prop">
        <td class="name" valign="top">
            <label for="customer">Purpose: </label>
        </td>
        <td clas="value" valign="top">
            <input type="text" id="purpose"
                   name="${jbpm.ctxVariable('expenseClaim.purpose')}"
                    value="${expenseClaim?.purpose?.encodeAsHTML()}"/>
        </td>
    </tr>
    <tr class="prop">
        <td class="name" valign="top">
            <label for="product">Date:</label>
        </td>
        <td clas="value" valign="top">
            <g:datePicker name="${jbpm.ctxVariable('expenseClaim.date')}" value="${expenseClaim?.date}" precision="day"/>
        </td>
    </tr>
    <tr class="prop"> 
        <td class="name" valign="top"><label for="product">Amount:</label> </td> 
        <td clas="value" valign="top"> 
            <input type="text" id="amount" name="${jbpm.ctxVariable('expenseClaim.amount')}"   
                    value="${expenseClaim?.amount?.encodeAsHTML()}"/> 
         </td> 
    </tr> 
</table>

Also create the process wide templates for the list and details views. Create the file "_list_header.gsp" under the folder "grails-app/views/jbpm/templates/Expense Reimbursement" with the following contents

Download

<tr>
      <th>ID</th>
      <th>Purpose</th>
      <th>Date</th>
      <th>Amount</th>
      <th>Filed Date</th>
  </tr>

Create the file "_list_body.gsp" under the folder "grails-app/views/jbpm/templates/Expense Reimbursement" with the following contents

Download

<tr>
    <td><g:link action="details" id="${taskInstance.id}">${taskInstance.id}</g:link></td>
    <td>${taskInstance.variables['expenseClaim']?.purpose}</td>
    <td>${taskInstance.variables['expenseClaim']?.date}</td>
    <td>${taskInstance.variables['expenseClaim']?.amount}</td>
    <td>${taskInstance.create}</td>
</tr>

Create the file "_details.gsp" under the folder "grails-app/views/jbpm/templates/Expense Reimbursement" with the following contents

Donwload

<table>
    <tr class="prop">
        <td class="name" valign="top">
            <label for="customer">Purpose: </label>
        </td>
        <td clas="value" valign="top">
            <input type="text" id="purpose"
                   name="${jbpm.ctxVariable('expenseClaim.purpose')}"
                    value="${expenseClaim?.purpose?.encodeAsHTML()}"/>
        </td>
    </tr>
    <tr class="prop">
        <td class="name" valign="top">
            <label for="product">Date:</label>
        </td>
        <td clas="value" valign="top">
            <g:datePicker name="${jbpm.ctxVariable('expenseClaim.date')}" value="${expenseClaim?.date}" precision="day"/>
        </td>
    </tr>
    <tr class="prop"> 
        <td class="name" valign="top"><label for="product">Amount:</label> </td> 
        <td clas="value" valign="top"> 
            <input type="text" id="amount" name="${jbpm.ctxVariable('expenseClaim.amount')}"   
                    value="${expenseClaim?.amount?.encodeAsHTML()}"/> 
         </td> 
    </tr> 
</table>

You can test drive the process now. Login as employee and navigate to the following url

http://localhost:8080/Expense/task/list

Click on the "New Expense Claim" link, fill out the details, choose the action as "Save As Draft" and click done. This would instantiate a new process. The newly created process would create a new task entry for "Draft". Click on the "Draft" link on the left pane. The newly created claim will be shown in the list. You can click on the ID to bring up the details column, which will bring up the Expense claim details form. Choose the action as "Submit" and click on done. This would submit the claim for manager review. You can now login as manager and see this expense claim listed under the "Manager Review" task.

The list and detail page can be customized per task basis by creating a folder with the task name and creating the files _list_header.gsp, _list_body.gsp and _details.gsp under that folder. This is required for tasks where the fields to be shown are different or some of the fields need to be made non-editable.

TODO - include more details on the convention used to locate the templates.

Download Sample Application

Roadmap

  • Add pagination & sorting support for the task list
  • Validation support for create & details forms
  • Add support for implementing action, decision and assignment handlers as grails artifacts
  • Redeploy process only if it has changed
Author(s):
  • Kamal Govindraj (kamal at tenxperts.com)
  • Chetan Mehrotra (chetan.mehrotra@gmail.com)