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:
- Workflow designer places a Service Task with
flowable:delegateExpression="${generatePdf}"and writes the HTML template inline. - At runtime, the delegate reads the
templatefield and performs simple${var}regex replacement using process variables. - Flying Saucer converts the rendered HTML+CSS into a PDF byte stream.
- The PDF is saved to
uploads/{processInstanceId}/{uuid}_{outputFileName}.pdfvia the existingStorageService. - The generated filename is stored as a process variable with the name specified by
outputVariable. - A
ProcessAttachmentrecord 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
generatePdfService 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 atemplatefield, it is treated as an Error and blocks deployment.