| sidebar_position | title | description |
|---|---|---|
2 |
tSM SpEL – Quick Reference & Deep Dive |
A single, authoritative guide to the tSM Spring Expression Language – from the very first keystroke in the console to sophisticated, production-grade scripts. |
The tSM Spring Expression Language (SpEL) couples a clean, readable syntax with the full power of Java, giving you an expressive way to automate anything inside the tSM platform.
This page is your one-stop reference: a quick tour of every major SpEL concept and a thorough specification of every syntax rule, console shortcut and best-practice pattern.
If you are completely new to SpEL you can skim the Essentials in a Nutshell section first and then jump straight to the detailed syntax reference further below whenever you need the fine print.
| Shortcut | Action |
|---|---|
Ctrl + Space |
Open autocomplete suggestions. |
Ctrl + Space twice |
Show a description of the highlighted suggestion. |
Ctrl + Enter |
Evaluate the entire script. |
Select text + Ctrl + Enter |
Evaluate only the selected expression. |
Select documentation + Enter |
Insert example snippet of selected parameter. |
// Example script – write as a whitespace-separated list of expressions
#orderTotal = 250
#discountedTotal = #orderTotal - 50
#date = #now().formatted('dd-MM-yyyy HH:mm')
#user = @user.user.get(#currentUser()).name
#orderSummary = 'Price: ' + #discountedTotal + '; Date: ' + #date + '; User: ' + #user
Hint Most snippets in this guide can be pasted straight into the SpEL console. Use the little icon that appears in the top-right corner of each block.
Note A script is simply one or more expressions separated by whitespaces. Result of entire script is the result of the last command.
// Literals
'Hello World' // String
1234 // Decimal number
123.4 // Floating-point number
0x7FFFFFFF // Hexadecimal
6.0221415E+23 // Scientific notation
true // Boolean
null // Null
Assigning values to variables for later use in expressions. Operator: =
#myVariable = 'Some Value',
#anotherVariable = 42,
#customer = @customerPublicService
Combining literals and variables to create dynamic strings. Operator: +
#name = 'John Doe',
#greeting = 'Hello, ' + #name + '!'
Different types of data that can be used in tSM SpEL. Commonly used data types are:
#myString = 'Hello world!' // String
#myInteger = 123 // Integer
#myDouble = 123.45 // Double
#myBoolean = true // Boolean
#myList = ['order', 'ticket', 'process'] // List
#myMap = {'customerName':'Alpha', 'employees':100} // Map
#myObject = @user.user.get(#currentUser()) // Object
#myJsonNode = '{"name":"John""age":30}'.toJsonNode() // JsonNode
#mySpreadsheet = #spreadsheet // Spreadsheet
#myDate = #now() // Date
#myUUID = #randomUUID() // UUID
#myNull = null // Null
Note Under the hood, tSM SpEL uses Java and Kotlin. For example,
Stringis a JavaString,Listis a JavaList, and so on. For security reasons, you cannot use any Java class or method directly in SpEL. We only allow a limited set of classes and methods that are safe to use in the tSM environment. However, general rules of Java apply, so you can consult Java and Kotlin documentation for more details on behaviour of standard classes and methods.
Used to perform arithmetic operations. Operators available: +, -, *, ^, / or div, % or mod
#addition = 1 + 2 // 3
#subtraction = 5 - 2 // 3
#multiplication = 3 * 4 // 12
#exponentiation = 2 ^ 3 // 8
#division = 10 / 3 // 5 —or— 10 div 2
#modulus = 10 % 3 // 1 —or— 10 mod 3
Used to compare two values. Operators available: == or eq, != or ne, < or lt, > or gt, <= or le, >= or ge
#isEqual = 1 == 1, // true —or— 1 eq 1
#isNotEqual = 1 != 2, // true —or— 1 ne 2
#isLessThan = 1 < 2, // true —or— 1 lt 2
#isGreaterThan = 2 > 1, // true —or— 2 gt 1
#isLessThanOrEqual = 1 <= 1, // true —or— 1 le 1
#isGreaterThanOrEqual= 2 >= 1, // true —or— 2 ge 1
#isEqualString = 'is' == 'is',
#isNotEqualString = 'is' != 'not'
Used to perform logical operations. Operators available: && or and, || or or, ! or not
#logicalAnd = true && false, // false —or— true and false
#logicalOr = true || false, // true —or— true or false
#logicalNot = !true // false —or— not true
Accessing values in 'Maps' and 'Objects' by their 'Keys' or 'Properties'. Operators: ., ['key']
#myMap = {'name':'Your Company', 'address':{'street':'Na padesátém','city':'Prague'}}
#myMap.name // "Your Company" —or— #myMap['name']
#myMap.address.street // "Na padesátém" —or— #myMap['address']['street']
Accessing 'Items' in a 'List' by their index. Operator: [index]
#myList = {'order', 'task', 'process'}
#myList[0] // "order"
Collection Selection: Selecting items from a collection based on specific criteria. Operator: .?[condition]
Collection Projection: Projecting specific properties of items in a collection. Operator: .![key]
// Collection Selection and Collection Projection
// You have this 'List of Maps', for example:
#services = [{'name': 'Int', 'price': 10}, {'name': 'TV', 'price': 15}, {'name': 'Phone', 'price': 5}]
// Selecting services with price greater than 9
#sel = #services.?[price > 9] // [{'name': 'Int', 'price': 10}, {'name': 'TV', 'price': 15}]; Collection Selection
// Projecting the names of the selected services
#serviceNames = #sel.![name] // ['Int', 'TV']; Collection Projection
// Or you can combine them in one step
#selectedServiceNames = #services.?[price > 50].![name] // Combined selection and projection
#selectedServiceNames // ['Int', 'TV']
Tip As the result of the script is the last expression, to evaluate only #sel, select the script up to the row and hit
Ctrl + Enter. Or you can use debugger to see partial results.
Controlling the flow of execution in expressions.
Condition-based flow control operators allow you to execute expressions based on specific conditions, providing a way to handle different scenarios dynamically. Operators available:
#if(cond1).then(exp1).elseif(cond2).then(exp2).else(exp3)#match(var1).when(valueOfVar1,exp1).else(exp2)#case().when(cond1,exp1).else(exp2)
// If-then-elseif-then-else Flow Control
#total = 99
#if(#total > 200)
.then( #discount = 0.20 )
.elseif(#total > 100)
.then( #discount = 0.10 )
.else( #discount = 0.05 ) // 0.05
// Match-when-else Flow Control
#technologyAvailable = '5G',
#match(#technologyAvailable)
.when('5G', #provision = 'mobile')
.else( #provision = 'fix') // 'mobile'
Block-based flow control operators allow you to group expressions and execute them together, making it easier to manage complex logic. Operators available:
#with(exp1,exp2).do(exp3)#do(exp1, exp2)
#with(
#orderTotal = 250,
#orderDiscount = 0.10,
#discountedTotal = #orderTotal - (#orderTotal * #orderDiscount)
).do(
#orderType = #case()
.when(#orderTotal > 100, 'Large Order')
.else('Small Order')
) // 'Large Order'
Iteration operators
allow you to loop through collections and execute expressions for each item, providing a way to handle repetitive tasks. Operators available:
.forEach(var1, exp1, exp2)
#numbers = [1, 2, 3, 4]
#squares = []
#numbers.forEach(#num, #squares.add(#num * #num)) // [1, 4, 9, 16]
The ternary operator is a concise way to perform conditional logic in place of an if-then-else statement. It simplifies the code and enhances readability, especially for simple conditions.
#orderTotal = 150
#orderType = #orderTotal > 100 ? 'Large Order' : 'Small Order'
The null safety operator helps prevent NullPointerExceptions by allowing safe navigation through potential null references.
// Safe navigation through potential null references - Null Safety Operator
#order = {'customer': null}
#customerName = #order.customer?.name // null if customer is null
The Elvis operator provides a shorthand for assigning default values when expressions evaluate to null.
// Default value assignment using Elvis Operator
#customerName = null
#displayName = #customerName ?: 'Unknown Customer' // 'Unknown Customer'
The try-catch construct allows handling exceptions within SpEL expressions.
// Exception handling using Try-catch Flow Control
#result = #try(
#riskyOperation()
).catch(
'Error occurred'
)
There are two kinds of built-in functions:
-
Type extensions – methods that extend everyday data types such as strings, numbers, dates, lists, sets and maps. They read naturally:
'Hello'.uppercase() // "HELLO" [1,2,3].sum() // 6 (example only) -
Standalone functions – utilities that are not tied to any specific value and are invoked with the
#prefix:#now() // current date-time #randomUUID() // e.g. "8e29…" #if(#total > 100).then('Large')...
See 07_SpEL_builtin_functions.md for the full catalogue.
Context variables provide access to various aspects and provide informarmation about the current execution context in tSM.
// Context Variables
#orderProductCodes = #order.productCodes() // Get list of product codes processed within order
#completedTask = #task.complete() // Finish the current task and move process forward
#processInfo = #execution.processInstance // Get information about running Process
#ticketStatus = #ticket.status // Retrieves status of current running ticket
tSM services are powerful tools that allow you to interact with various functionalities within the tSM platform. Using them you can manipulate every entity of the tSM platform, create various API calls and use them as part of integration with another platfrom. To these there are about 1500 methods of tSM services available, so they can't be listed here, but we will look at them deeper in chapter III. Standard syntax of tSM Service is @tsmService.tsmMethod.
#currentUser = @user.user.get(#currentUser()),
#customer = @tsmDatabaseClient
.query("select id,key from crm.crmt_customer")
.execute()
.last()
.id,
#account = @crm.person.geByCustomerId(#customer)
Hint: To explore tSM Services, you can leverage the autocomplete option of the tSM Console or open the 'tSM Public API' item in the main menu of the tSM Platform. Here, you will find the Swagger documentation for all available services, as well as Public API operations.
Class expressions allow you to access static methods and fields of a class directly within SpEL.
#sqrtResult = T(java.lang.Math).sqrt(16) // 4
#matches = '192.168.0.2'
.matches('^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$')
// true
// Primitive values
#orderTotal = 250
#orderDiscount = 0.10
#orderStatus = 'Pending'
#notificationSent = false
#priority = false
#customerName = 'John Doe'
// Collections
#adminEmails = ['[email protected]', '[email protected]']
#services = [
{'name':'Internet','price':100},
{'name':'TV','price':150},
{'name':'Phone','price':50}
]
// Calculations
#discountedTotal = #orderTotal - (#orderTotal * #orderDiscount)
#isLargeOrder = #orderTotal > 100
// Ternary & Elvis
#orderType = #isLargeOrder ? 'Large Order' : 'Small Order'
#isPriority = #isLargeOrder ? true : false
#customerNameSafe = #customerName ?: 'Unknown Customer'
// Collection ops
#selectedServices = #services.?[price > 50]
#serviceNames = #services.![name]
// Flow control & service call
#sendNotification = #if(#isPriority and !#notificationSent).then(
#adminEmails.forEach(
#email,
@notificationPublicService.sendNotification({
"templateCode": "TaskAssigned",
"ownerId": #currentUser(),
"ownerType": "Order",
"notificationTo": [
{"ownerId": #currentUser(), "ownerType": "User"},
{"email" : "[email protected]"}
],
"data": { "order": #order, "task": #task }
})
),
#notificationSent = true
).else('Notification not needed')
#result = {
'Total Amount' : #orderTotal.toString(),
'Discounted Total' : #discountedTotal.toString(),
'Order Status' : #orderStatus,
'Order Type' : #orderType,
'Priority Order' : #isPriority,
'Customer Name' : #customerNameSafe,
'Services' : #serviceNames.toString(),
'Selected Services' : #selectedServices.toString(),
'Notification Sent' : #notificationSent
}
| Mode | How to open | When to use |
|---|---|---|
| Without context | In the main left menu search for spel and select SpEL Console. |
Quick experiments, standalone utilities. |
| With context | Open an entity (order, ticket, …), click the ⋯ menu in the top-right corner and choose SpEL Console. | When you need context variables such as #order or #ticket. |
Ctrl + Space– suggestions for partially typed items.@thenCtrl + Space– list all tSM services.#thenCtrl + Space– list standalone functions, variables and flow operators.- Double
Ctrl + Space– show a description of the selected suggestion. Enterinside()– insert example arguments for a method.
Ctrl + Enter– evaluate the whole console buffer.- Select +
Ctrl + Enter– evaluate only the highlighted expression (one at a time).
Using these shortcuts dramatically speeds up your workflow & reduces typos.
A script is a sequence of expressions, each separated by a comma. You can write those expressions:
- Inline, without delimiters – the most common form.
- Inside
{ … }or[ … ]– helpful when you embed a script inside another expression.
#first = 'Hello ',
#second = 'World',
#third = #first + #second // "Hello World"
Rule of thumb: Any syntactically valid expression that returns exactly one value—be it a number, a string, a map or a whole object graph—counts as one expression.
Every expression except the last must end with a comma:
#one = 'A', // comma
#two = 'B', // comma
#three = #one + #two // no comma – this is the last expression
Use = to assign.
Re-assigning the same variable implicitly changes its data type and overwrites the previous value.
#value = 3,
#value = 'now I am a string'
-
Standalone functions & operators begin with
#when they are the first item in a chain.#now() #if(condition).then(expr1).else(expr2) -
Chained methods start with
..'some text'.uppercase() -
Services start with
@and expose their own methods.@customerPublicService.findCustomer(#idOrKey)
Every flow-control/operator body is enclosed in ():
#with(body).do(otherBody)
- Curly braces
{}– optional script delimiter or to create an ad-hoc block when nesting scripts. - Square brackets
[]– list/array literals and positional access. - Parentheses
()– method arguments, grouping sub-expressions.
- Operators & syntax – see Sections 1.7-1.10 above.
- Built-in helpers – full list in 07_SpEL_builtin_functions.md.
- All tSM services – swagger docs under tSM → Public API.
- Console shortcuts – Section 2.1 & 2.2.
Happy scripting — and may your expressions always evaluate exactly as expected!