15. Service Tasks (Automation)

Service tasks allow you to execute system logic automatically. You refer to a specific operation using the flowable:delegateExpression attribute.

📋 Available Delegates Summary

Delegate Name Bean Name (${...}) Description Required Fields (<flowable:field>)
Send Reminder sendReminder Sends an email notification. to (email), subject, body
Reassign Task reassignTask Changes assignment of active tasks (e.g., for escalation). targetAssignee (or targetCandidateGroups), clearExisting
Assign by Role assignUsersByRole Finds all users with a role and returns a list of emails. roleName, outputVariable (result var name)
Generate PDF generatePdf Generates a PDF document from an inline HTML template. template (HTML), outputVariable (result var name)

Available Operations

1. Fetch Users By Role (assignUsersByRole)

Fetches all users with a specific role and stores their emails in a list variable. This is primarily used for Parallel Multi-Instance tasks (assigning a task to everyone in a role).

Inputs (Fields):

  • roleName: The role key (e.g., ROLE_MANAGER).
  • outputVariable: Name of the variable to store the list in (default: targetUsers).

Example: Assign Survey to All Managers

<!-- Step 1: Fetch list of managers -->
<serviceTask id="fetchMgrs" flowable:delegateExpression="${assignUsersByRole}">
    <extensionElements>
        <flowable:field name="roleName" stringValue="ROLE_MANAGER"/>
        <flowable:field name="outputVariable" stringValue="managerList"/>
    </extensionElements>
</serviceTask>

<!-- Step 2: Create a task for EACH manager in the list -->
<userTask id="survey" name="Manager Survey" flowable:assignee="${assignee}">
    <multiInstanceLoopCharacteristics isSequential="false"
         flowable:collection="managerList"
         flowable:elementVariable="assignee"/>
</userTask>

2. Generate PDF Document (generatePdf)

Generates a PDF document from an inline HTML template with ${variable} placeholders. The template is written directly inside the BPMN Service Task as a flowable:field — no external files, no template management.

Inputs (Fields):

Field Required Description
template ✅ Yes Inline HTML content with ${variable} placeholders. Written inside a <flowable:string><![CDATA[...]]></flowable:string> block.
outputVariable ✅ Yes Name of the process variable that will hold the generated filename. Used to reference the PDF in subsequent tasks.
outputFileName ❌ Optional Human-readable part of the PDF filename. Defaults to document. A UUID prefix is always prepended to avoid collisions (e.g., a8f3b2c1_invoice.pdf).

How It Works:

  1. Workflow designer places a Service Task with flowable:delegateExpression="${generatePdf}" and writes the HTML template inline.
  2. At runtime, the delegate reads the template field and performs simple ${var} regex replacement using process variables.
  3. Flying Saucer converts the rendered HTML+CSS into a PDF byte stream.
  4. The PDF is saved to uploads/{processInstanceId}/{uuid}_{outputFileName}.pdf via the existing StorageService.
  5. The generated filename is stored as a process variable with the name specified by outputVariable.
  6. A ProcessAttachment record is created so the PDF automatically appears in the "Process Documents" card for all subsequent User Tasks — no form field needed.

Example: Generate an Invoice PDF

<!-- Step 1: Employee enters invoice data -->
<userTask id="fillData" name="Enter Invoice Details" flowable:assignee="${initiator}">
    <extensionElements>
        <activiti:formProperty id="itemName" name="Item Name" type="string" required="true"/>
        <activiti:formProperty id="price" name="Price" type="double" required="true"/>
        <activiti:formProperty id="buyDate" name="Purchase Date" type="date" required="true"/>
    </extensionElements>
</userTask>

<!-- Step 2: Generate PDF (automatic, no human interaction) -->
<serviceTask id="genInvoice" flowable:delegateExpression="${generatePdf}">
    <extensionElements>
        <flowable:field name="outputVariable" stringValue="generatedInvoice"/>
        <flowable:field name="outputFileName" stringValue="invoice"/>
        <flowable:field name="template">
            <flowable:string><![CDATA[
                <html><body>
                <h1>Invoice</h1>
                <p>Item: ${itemName}</p>
                <p>Price: ${price} USD</p>
                <p>Date: ${buyDate}</p>
                </body></html>
            ]]></flowable:string>
        </flowable:field>
    </extensionElements>
</serviceTask>

<!-- Step 3: Manager reviews — PDF is automatically visible in the "Process Documents" card -->
<userTask id="reviewInvoice" name="Review Invoice" flowable:candidateGroups="ROLE_MANAGER">
    <extensionElements>
        <activiti:formProperty id="decision" name="Decision" type="enum" required="true">
            <activiti:value id="approve" name="Approve"/>
            <activiti:value id="reject" name="Reject"/>
        </activiti:formProperty>
    </extensionElements>
</userTask>

Automatic visibility: The generated PDF appears in the "Process Documents" card on every User Task after the generatePdf Service Task — no form field is needed. This is the same pattern used for user-uploaded files.

Template format: Templates support HTML + CSS 2.1 (Flying Saucer limitation). To include images (e.g., company logo), embed as base64 data URI (<img src="data:image/png;base64,..." />).

Validation: If a BPMN uses ${generatePdf} without a template field, it is treated as an Error and blocks deployment.