Skip to main content
Version: 2.4

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​

  1. Trigger — a user action or API call creates, updates, or deletes an entity.
  2. Matching — the platform finds all binding rows where entityType and eventName match the event. All matching bindings are executed (ordered by script code ascending).
  3. 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.
  4. 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):

PropertyTypeRequiredDescription
entityTypestring✔Logical name of the entity that raises the event (e.g. Ticket, Order). Autocomplete comes from the built-in Entity-Type LOV widget.
eventNamestring✔Lifecycle event – one of CREATE, UPDATE, DELETE.
scriptstring✔Code of the SpEL script to execute, chosen via the Script LOV.
asyncboolean–Run async: dispatch the event after the DB transaction commits (good for heavy work). Default =false (synchronous).
privateEntityboolean–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​

  1. Register row – as above.

  2. Script – create a new entry in Scripts with code Ticket.Events.DescriptionToUppercase and 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(), …).
Transaction deep dive

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 → #ticket
  • Order → #order
  • ProcessInstance → #processInstance

When the binding row has privateEntity=true an additional variable with a Private suffix appears, pointing to the managed Hibernate instance:

  • Ticket → #ticketPrivate
  • Order → #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 traceId MDC 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​

PatternAdvice
Heavy IO (HTTP, mail)Mark binding async=true or enqueue work via @taskScheduler.
Long-running loopsBatch queries; aim for < 100 ms per event.
Private vs public entityPrefer 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​