Event Bindings
An event binding is a SpEL script that tSM automatically executes when a predefined platform event occurs. Think of it as a server-side trigger written in SpEL.
How it works
- Trigger — a user action or API call creates, updates, or deletes an entity.
- Matching — the platform finds all binding rows where
entityTypeandeventNamematch the event. All matching bindings are executed (ordered by script code ascending). - Synchronous (
async=false, default) — the script runs inside the same database transaction as the entity change. If the script fails, the entire change is rolled back. - Asynchronous (
async=true) — the script is dispatched after the transaction commits. It receives a read-only snapshot; failures are retried with exponential back-off before landing in the dead-letter queue.
Register-based Configuration
Bindings are configured in the register "Scripts / Bindings / Entity Events" (internal code Scripts.Bindings.EntityEvents). Each row corresponds to one binding and is described by the following JSON schema (simplified):
| Property | Type | Required | Description |
|---|---|---|---|
entityType | string | ✔ | Logical name of the entity that raises the event (e.g. Ticket, Order). Autocomplete comes from the built-in Entity-Type LOV widget. |
eventName | string | ✔ | Lifecycle event – one of CREATE, UPDATE, DELETE. |
script | string | ✔ | Code of the SpEL script to execute, chosen via the Script LOV. |
async | boolean | – | Run async: dispatch the event after the DB transaction commits (good for heavy work). Default =false (synchronous). |
privateEntity | boolean | – | Use internal model: pass the private (managed) entity instance to the script instead of the documented public model. Use only for low-level tweaks; the internal API may change without notice. |
Example binding (as stored in the register):
{
"entityType" : "Ticket",
"eventName" : "UPDATE",
"script" : "Ticket.Events.DescriptionToUppercase",
"async" : false
}
Minimal Working Example
-
Register row – as above.
-
Script – create a new entry in Scripts with code
Ticket.Events.DescriptionToUppercaseand source:#ticket.description = #ticket.description.uppercase()
Save both. The next time a Ticket UPDATE happens, the description is converted to uppercase.
Authoring the SpEL Script
The binding row only tells which script to run. The script itself lives in the tSM Scripts register (main menu → Scripts). See the Script reference for the full definition and attribute reference, and the SpEL Console guide for developing and debugging scripts interactively.
- Code – must match the value used in the binding.
- Source – the actual SpEL text.
Organization – keep event scripts short and focused. If you need more than ~50 lines, delegate into a service class or another script-to-script helper.
Execution Context & Transactionality
- Scripts run inside the same DB transaction as the change that triggered them – unless
async=true, in which case they execute right after commit (read-only snapshot; failures are retried × 3 with exponential back-off before landing in the dead-letter queue). - A context variable with the name of the entity type (e.g.
#ticket) is available in the script. - All standard SpEL helpers work (
#now(),#if(),#with(), …).
For a thorough explanation of how transactions affect SpEL scripts — including external calls, the {async: true} flag, and #businessException — see SpEL and Transactions and the Transactions overview.
Context Variables
For every binding tSM injects one primary context variable whose name is the entity type with the first letter lower-cased:
Ticket→#ticketOrder→#orderProcessInstance→#processInstance
When the binding row has privateEntity=true an additional variable with
a Private suffix appears, pointing to the managed Hibernate instance:
Ticket→#ticketPrivateOrder→#orderPrivate
The public model (#ticket, #order, …) is still available even in
private-entity bindings, so you can mix safe DTO reads with occasional direct
manipulations.
You are free to read and write the context object; any changes are flushed
to the database unless the binding is asynchronous (async=true) — in that
case the object is a detached, read-only snapshot and modifications are
not allowed.
Debugging & Testing
-
Kibana – every execution is logged; search by the
traceIdMDC key to correlate the full flow. -
Manual log in SpEL:
// Kibana based logging
@logger.info("Ticket description changed to " + #ticket.description)
// tSM Integration Logs
@log.log.create({
logTypeCode : "Test.Log",
request : "Ticket description changed to " + #ticket.description
})
Performance Tips
| Pattern | Advice |
|---|---|
| Heavy IO (HTTP, mail) | Mark binding async=true or enqueue work via @taskScheduler. |
| Long-running loops | Batch queries; aim for < 100 ms per event. |
| Private vs public entity | Prefer public DTO; set privateEntity=true only when you must bypass standard hooks. |
Advanced Patterns & Gotchas
Field-specific UPDATE bindings
Short-circuit early when the change is irrelevant:
#if(!#changedFields.containsKey('description')).then('skip')
Multiple bindings on the same event
All bindings matching (entityType,eventName) are executed.
Order is deterministic: ascending by the script code.
Async caveat
Async bindings receive a detached, read-only snapshot. Direct modifications to the entity are not allowed; use services instead.
For more details on async behavior and its interaction with the transaction model, see SpEL and Transactions — Async.
See also
- Script (reference) — script definition, types, parameter forms, and attributes.
- SpEL Console — developing and debugging SpEL scripts interactively.
- Script-to-Script Bindings — call one script from another.
- SpEL and Transactions — how transactions affect SpEL scripts.
- Process Engine Transactions — transaction boundaries in BPMN processes.