Workflow
Workflow Service runs BPMN 2.0 process definitions on an embedded Flowable process engine. It exposes process definitions, running/historic process instances, tasks, and dead-letter jobs as CREST resources.
- OSGi service class
-
org.wrensecurity.wrenidm.workflow.flowable.impl.FlowableServiceImpl - OSGi persistent identifier
-
org.forgerock.openidm.workflow - Configuration file
-
workflow.json - Router mapping
-
/workflow*
FlowableServiceImpl itself only proxies CREST requests to an inner FlowableResource request handler once the process engine has started.
Six cooperating resource classes implement the actual per-resource behavior.
REST API describes them.
Two other integrations shape how workflows behave in Wren:IDM, and this page covers them separately below.
Workflow resolves user/group identity against managed/user and managed/role instead of Flowable’s own identity tables (Identity Integration).
BPMN scripts and expressions can call into the Wren:IDM script registry and OSGi services (Scripting and Expression Integration).
Engine Configuration
conf/workflow.json{
"useDataSource" : "default",
"workflowDirectory" : "&{launcher.project.location}/workflow"
}
This block illustrates every configurable key, including ones with no Wren:IDM-applied default (history, mail.username, mail.password), shown here with an example value only.
It is not a recommended or default configuration in its own right.
conf/workflow.json (full schema){
"location" : "embedded",
"useDataSource" : "default",
"workflowDirectory" : "workflow",
"tablePrefix" : "",
"tablePrefixIsSchema" : false,
"history" : "audit",
"mail" : {
"host" : "localhost",
"port" : 25,
"username" : "mailUser",
"password" : "mailPassword",
"starttls" : false
},
"engine" : {
"url" : "",
"username" : "",
"password" : ""
}
}
location-
Where the process engine instance comes from. Type: String (
embedded,local). Default:embedded.embedded-
The service builds and starts its own
StandaloneProcessEngineConfiguration(see below). local-
The service binds to an externally-registered
ProcessEngineOSGi service instead of starting its own, using the reference target filter(!(wrenidm.flowable.engine=true)). AnyProcessEngineservice that does not carry that property is eligible. Wren:IDM does not itself register any otherProcessEngineservice, so a different bundle must publish one forlocalto have anything to bind to. The reference is optional, so the service still activates when no matching engine is present. It stays unable to serve workflow requests until one registers. Requests against/workflow/*return500 Internal Server Errorwhile unbound.The engine-location enum also declares a remotevalue, and the schema declaresengine/url,engine/username, andengine/passwordkeys. None of these are functional. SettinglocationtoremotethrowsInvalidExceptionat activation, andparseConfigurationnever readsengine/*. Do not configure either.
Except for location itself, Wren:IDM parses every field below from configuration regardless of location’s value, but applies it only when `location is embedded.
The local branch uses the externally-bound engine as-is and reads none of them.
useDataSource-
Name of the
DataSourceServiceinstance (matched by itsconfig.factory-pid-derived key) that backs the embedded engine’s tables (the same Repository JDBC data source service used elsewhere in Wren:IDM). Type: String. Default:default. A name that does not match any boundDataSourceServicecausesIllegalStateExceptionat first database access. workflowDirectory-
Directory that Wren:IDM watches for deployable process archives (see Process Deployment). Type: String. Default:
workflowwhen the key is absent from configuration entirely. The shippedconf/workflow.jsonsets it explicitly to&{launcher.project.location}/workflowinstead. This is a launcher-level substitution token that resolves to the Wren:IDM project instance directory (see the default configuration block above). tablePrefix/tablePrefixIsSchema-
Passed straight through to
StandaloneProcessEngineConfiguration.setDatabaseTablePrefix/setTablePrefixIsSchema. Type: String / Boolean. Default:""/false. history-
Flowable history level:
none,instance,task,activity,audit, orfull. Type: String. Wren:IDM applies no default for this key. When absent, Flowable’s own engine default ofauditapplies unchanged. Wren:IDM does not validate the configured value before it reaches the engine. It reads the value as a plain string with no allow-list or enum check, unlikelocation, which it parses against a fixed enum. An unrecognized value therefore passes through unvalidated. Wren:IDM neither rejects it nor substitutes a default value. mail-
Outbound mail server settings passed to the embedded engine’s mail task support. Type: Object.
host-
Type: String. Default:
localhost. port-
Type: Integer. Default:
25. username/password-
Type: String. No default. Wren:IDM encrypts
passwordat rest (see below). starttls-
Type: Boolean. Default:
false.
ConfigMeta marks mail/password (and the non-functional engine/password) as sensitive fields, so Wren:IDM automatically encrypts any value stored under either key when it writes the configuration.
Process Deployment
When location is embedded and a ConfigurationAdmin service is available, activation registers an Apache Felix File Install factory configuration.
This configuration watches the directory named by workflowDirectory (resolved via IdentityServer.getFileForInstallPath) for files matching .bar or .xml.
It polls every 2000 ms and starts newly-installed bundles automatically.
The poll interval is hardcoded at 2000 ms.
Configuration cannot change it.
Dropping a BAR (Business Archive) or a bare BPMN XML file into that directory deploys it to the running process engine without a restart. Files already present in the directory at first startup deploy on the initial poll the same way. There is no separate baseline-only first-run behavior. Removing a previously-deployed file from that directory undeploys it automatically on the next poll cycle (governed by Apache Felix File Install’s own semantics, not Wren:IDM’s). On deactivation, the service deletes the File Install configuration, so it no longer watches the directory.
The service also registers the running ProcessEngine itself as an OSGi service (tagged wrenidm.flowable.engine=true), so that deployment tooling built on top of the Flowable OSGi integration can reach it.
The service’s own ProcessEngine reference explicitly excludes services carrying that tag, to avoid binding to itself.
REST API
FlowableResource routes seven paths under /workflow to six resource classes.
| Path | Resource class |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Every query on these resources requires a _queryId of either ALL_IDS (list everything) or filtered-query (apply the request’s additional parameters and sort keys).
Any other query ID returns a BadRequestException.
Task Definitions is the sole exception to this.
It accepts only ALL_IDS and rejects filtered-query outright (see the Task Definitions subsection below).
Process Definitions
GET /workflow/processdefinition (filtered-query) accepts the following additional parameters:
-
deploymentId -
category(alsocategoryLike) -
_id(the process definition ID) -
key(alsokeyLike) -
name(alsonameLike) -
processDefinitionResourceName(alsoprocessDefinitionResourceNameLike) -
version
Every process definition response, from both the single-item read and the collection query, includes its own deploymentId field.
A reader can therefore discover valid filter values from any prior response, without needing outside knowledge of Flowable’s internal deployment ID scheme.
Reading a single process definition adds start-form data when the process declares a start form key:
-
the form key itself
-
the raw form template resource
-
the resolved form property handlers (id, expressions, type, readable/required/writable flags) Requesting the
diagramfield (_fields=diagram) also returns the process diagram image as a base64-encoded string, if the deployment includes one.
Unlike ProcessInstanceResource, TaskInstanceResource, TaskInstanceHistoryResource, and DeadLetterJobEntityResource, this resource does not implement applySortKeys.
Its queryCollection calls Flowable’s query.list() directly, with no sort-key handling of any kind.
It therefore silently ignores a _sortKeys parameter on its queries, rather than rejecting or applying it.
Deleting a process definition undeploys it (cascade=false).
The resource translates a PersistenceException from the underlying store (for example, when running instances still reference the definition) to a 409 Conflict.
Create, update, and patch are not supported on this resource.
Task Definitions
GET /workflow/processdefinition/{procdefid}/taskdefinition lists every BPMN UserTask element in the given process definition, each with its form key and form property handlers attached.
Its queryCollection accepts only _queryId=ALL_IDS and rejects any other value, including filtered-query, with a BadRequestException ("Unknown query-id").
Unlike every other resource on this page, it therefore has no additional filter parameters or _sortKeys support to document.
The query above always returns the full, unfiltered, unsorted list of UserTask elements for the given process definition.
Reading a single task definition by its BPMN element ID returns the same shape for that one task, or NotFoundException if no matching UserTask exists in the process.
This resource is read-only.
Create, update, delete, and instance-level action are all unsupported.
Process Instances
POST /workflow/processinstance starts a new process instance, either by process key (_key) or by a specific process definition ID (_processDefinitionId).
It also accepts an optional _businessKey.
When a request supplies both _key and _processDefinitionId, _processDefinitionId takes precedence.
The createInstance method starts the instance via startProcessInstanceById.
It extracts the supplied _key value from the request but never uses it.
Only when _processDefinitionId is absent does _key take effect, via startProcessInstanceByKey.
When a request supplies neither, _key resolves to null, and createInstance passes it through to startProcessInstanceByKey as-is.
What the underlying Flowable engine does with a null process key in that case is engine-internal behavior.
This page does not assert it, the same evidentiary boundary applied to the migrate and claim actions elsewhere on this page.
The remaining request body becomes the initial process variables.
Every started instance also receives an internal openidmcontext variable carrying a copy of the caller’s security context (authentication ID and authorization).
Scripts and expressions inside the process use it to act with the starting user’s identity (see Scripting and Expression Integration).
The resource strips this variable back out of every variable listing and read response before it reaches the REST caller.
GET /workflow/processinstance lists only unfinished instances.
GET /workflow/processinstance/history lists all historic instances regardless of completion.
The same ProcessInstanceResource class serves both routes, constructed with a different history query (unfinished() vs. no filter).
Every other operation (create, read, update, delete, action) behaves identically no matter which of the two paths reached it.
The following additional parameters apply to filtered-query:
-
processDefinitionId/processDefinitionKey -
processInstanceBusinessKey -
processInstanceId -
superProcessInstanceId -
businessStatus -
finished/unfinishedflags -
involvedUserIdandstartUserId -
startedAfter/startedBefore/finishedAfter/finishedBeforeISO-8601 timestamps -
var-<name>=<value>parameters that match against process variable values by exact equality (Flowable’svariableValueEqualsquery)
Unlike _businessKey, no caller can set businessStatus through this resource at all.
When a create request comes in, createInstance extracts only _key, _processDefinitionId, and _businessKey from it.
An update request instead hits updateInstance, which unconditionally returns a not-supported error.
The resource’s only instance action, migrate, does not touch it either.
A running instance’s business status can therefore come only from the process itself, through the embedded Flowable engine’s own API, never through this REST resource.
Sortable keys are processInstanceId, processDefinitionId, processInstanceBusinessKey, startTime, endTime, durationInMillis, and tenantId.
Reading a single instance includes its non-context process variables and the list of historic tasks that ran under it.
Requesting the diagram field also renders a base64-encoded PNG diagram (the same encoding used for the process-definition diagram) with the process instance’s currently active activities highlighted.
This applies only if the deployed process definition has a graphical BPMN notation.
Otherwise the resource silently omits the diagram field rather than causing an error.
Deleting an instance (DELETE, addressable from either path) cancels the running process via RuntimeService.deleteProcessInstance, recording the deletion reason "Deleted by Wren:IDM.".
It returns the instance’s last known historic state.
The migrate action (no request body) moves a running instance onto the process definition returned by Flowable’s latestVersion() query for the same process definition key.
It rejects the request if no instance or no later definition exists, or if the instance is already on the latest version.
All three rejection cases return a BadRequestException (each with a distinct message), so a caller can treat any 400 response from this action as one of those three named conditions.
A failure during the migration call itself instead returns a generic InternalServerErrorException.
Task Instances
GET /workflow/taskinstance (filtered-query) accepts the following additional parameters:
-
executionId -
processDefinitionId/processDefinitionKey -
processInstanceId -
assignee -
taskId/name/owner/description/priority -
taskCandidateGroup(comma-separated for multiple groups, see Identity Integration for a caveat on what these IDs actually reference) -
taskCandidateUser -
taskCandidateOrAssigned(a user ID, matched against either the assignee or the task’s candidates) -
unassigned(a boolean flag string where only the valuetrue, case-insensitive, activates the filter) -
tenantId -
var-<name>process-variable filters, matched by exact equality (Flowable’sprocessVariableValueEqualsquery)
Supplying multiple comma-separated groups to taskCandidateGroup matches a task if it is a candidate for any one of the listed groups.
It does not require the task to be a candidate for all of them.
Sortable keys are _id, name, description, priority, assignee, processInstanceId, executionId, createTime, dueDate, and tenantId.
Reading a task adds its form properties, current variables (openidmcontext excluded), the assignee (or, when a delegation is pending, the delegate instead), and its candidate users/groups.
This resource has no way to place a task into delegation itself.
It only reads DelegationState.PENDING, never sets it, so a task reaches that state only through the BPMN process definition or the Flowable engine directly.
Updating a task can change assignee, description, name, and owner.
The resource ignores other fields.
Deleting a task accepts an optional deleteReason additional parameter.
This resource supports two instance actions:
-
claim(body:{ "userId": "…" }) – assigns the task to that user -
complete(body: process variables to set) – finishes the task, propagating local variables to the parent scope
The claim action calls Flowable’s TaskService.claim(resourceId, userId) directly.
Wrenidm’s own code does not check the task’s current assignee beforehand, and it has no separate branch for an already-assigned task.
Whether TaskService.claim itself succeeds unconditionally or only on an unclaimed task is Flowable engine-internal behavior.
This page does not assert it, the same evidentiary boundary applied to the migrate action above.
Task Instance History
GET /workflow/taskinstance/history exposes the same task instances through Flowable’s history API.
It is query- and read-only.
Create, update, delete, and action all return notSupportedOnInstance/notSupportedOnCollection.
The following additional parameters apply to filtered-query:
-
executionId -
processDefinitionId/processDefinitionKey -
processInstanceId -
assignee -
taskCandidateGroup(comma-separated) -
taskCandidateUser -
taskId -
taskName(note:nameonTaskInstanceResourceabove, nottaskName) -
owner -
description -
finished/unfinishedandprocessFinished/processUnfinishedflags -
priority -
deleteReason -
tenantId -
var-<name>(exact equality, viaprocessVariableValueEquals)
As with TaskInstanceResource above, supplying multiple comma-separated groups to taskCandidateGroup matches a task if it is a candidate for any one of the listed groups.
It does not require the task to be a candidate for all of them.
Unlike TaskInstanceResource, taskCandidateOrAssigned and unassigned are not supported here.
Sortable keys are taskId, taskName, description, priority, assignee, processInstanceId, executionId, tenantId, _id (maps to orderByHistoricActivityInstanceId(), a different sort than TaskInstanceResource’s own `_id, which maps to orderByTaskId()), processDefinitionId, durationInMillis, startTime, endTime, owner, dueDate, deleteReason, and taskDefinitionKey.
Dead Letter Jobs
GET /workflow/job/deadletter lists jobs that exhausted their retry count, filterable by jobId, executionId, processDefinitionId, and processInstanceId, and sortable by createTime, executionId, _id, processInstanceId, and retries.
Reading a single dead-letter job by ID returns its job representation, or NotFoundException if no such job exists.
The retry action (no request body) moves a dead-letter job back to the executable job queue with one retry attempt.
This resource does not support create, update, delete, and collection-level action.
Identity Integration
A custom IdmEngineConfigurator wires in IdmIdentityService to replace the embedded engine’s identity component.
It reads Flowable users from managed/user and Flowable groups from managed/role instead of Flowable’s own identity tables.
Flowable’s notion of a user’s "ID" corresponds to the managed/user username, not the object’s _id.
A user lookup queries managed/user with the for-userName query and a uid parameter.
A group (role) membership lookup instead reads the authzMembers relationship field of the matching managed/role entry.
Looking up a user’s groups reads the authzRoles relationship field of the matching managed/user entry via the same for-userName query.
A plain group search without a member filter queries managed/role directly.
Because of this, taskCandidateUser/assignee values are managed/user usernames.
Likewise, taskCandidateGroup values are the bare _id of whatever role object backs an entry in a user’s authzRoles relationship.
A direct group search (not scoped to a user) always queries managed/role only.
However, authzRoles itself is schema-permitted to reference either managed/role or repo/internal/role.
A candidate group ID surfaced through a user’s roles is therefore not guaranteed to be a managed/role ID.
This identity bridge is read-only.
Every write operation on the identity service (creating or saving users/groups, memberships, privileges, tokens, pictures, and user info) throws UnsupportedOperationException.
Scripting and Expression Integration
BPMN script tasks and listeners resolve an openidm script binding through a custom ResolverFactory (IdmScriptResolverFactory).
This factory looks up a script from the Wren:IDM script registry, using the script task’s declared language.
It falls back to Groovy when it cannot determine the language.
It constructs the binding’s variables from a FlowableContext wrapping the process’s openidmcontext security context variable.
When a sub-execution has no openidmcontext of its own, the factory copies it down from the parent execution so nested scopes keep the same caller identity.
BPMN expressions (EL) can also reference any bound OSGi JavaDelegate service by its component name.
A custom EL resolver (IdmELResolver), registered ahead of Flowable’s default resolvers by IdmExpressionManager, supplies this.
Delegates bind and unbind dynamically as the corresponding OSGi services come and go.