Skip to content
Art2link ESB v2.02 LTS HomeDocumentationBlogContact
Core concepts/Variables

Variables

Application-scoped, dynamically-typed slots that pipelines, ports, and maps can read and write - for loop counters, captured XPath fragments, configuration constants, and anything else that needs to outlive a single component.

A variable in Art2link ESB is a named value defined under an Application, alongside schemas and message types. It does not belong to any one message; it belongs to the application, and any port, pipeline component, map, or routing expression in that application can read it or write to it.

Variables carry whatever the integration needs to remember between stages: a counter for a looping pipeline, a fragment captured out of the inbound payload by an XPath, a whole message held for later wrapping, an environment-specific endpoint, a correlation key. Anywhere a string can be typed in the platform, a variable can be referenced with {{Variable.Name}}.

A variable does not travel with the message. It lives at the application level, and every port and pipeline running inside that application sees the same variable. A receive port writing to CurrentBatchId and a send port reading {{Variable.CurrentBatchId}} a moment later are reading and writing the same slot.

This is the reverse of how promoted properties work. A promotion is a value lifted out of a specific message's payload; it is meaningful only while that message is being processed. A variable is a slot the application owns, and the runtime is what changes its value as messages move through ports.

Practical effect. Use a promotion when the value is intrinsic to a single message - a CustomerId on this Invoice, a Currency on that Order. Use a variable when the value is a piece of state the integration carries forward - a loop counter, a captured fragment for later use, or a configuration string set once and consumed many times.

Variables are scoped to an Application. From the application menu, the Variables list shows every variable defined in that application. Each entry has these fields:

FieldPurpose
NameHow the variable is referenced everywhere else - in port assignments, pipeline component property values, map bindings, custom function arguments, and routing expressions. Names are flat under the Variable namespace, so they must be unique within the application.
DescriptionFree-text description for human readers. Optional, has no runtime effect.

The list view follows the platform's Selected Application behavior - with an Application selected, the list filters to it and the Application column is hidden; with no selection, the list spans every Application you have access to and the column is shown. Creating a new variable uses the same flow: with an Application selected, the form opens with that Application preemptively chosen; without one, the form exposes an Application picker.

No data type field. Variables are deliberately untyped at declaration time - the slot is allocated, but its shape is decided by what gets written into it. See Dynamic typing below.

A variable does not declare a data type at design time. The runtime type is whatever the most recent assignment produced - the same slot can hold a string in one stage, a node-set in another, and a whole XML document in a third.

This mirrors the way promotions resolve: an XPath that returns a single text node yields a scalar, one that returns a node-set yields a list, one that returns an inner element yields a sub-tree, and an XPath of / yields the whole message. A variable assignment using an XPath inherits the same shape spectrum.

Source of the assigned valueVariable holds
A literal string in the port's variable assignment UIScalar string
An XPath that resolves to a single text node or attributeScalar string
An XPath returning multiple matchesList (node-set)
An XPath returning an element with childrenSub-tree
An XPath of /The whole message
A custom function callWhatever the function returns
A pipeline component setting {{Variable.Name}} directlyWhatever the component writes

Downstream consumers see the value as it currently is. A map that reads {{Variable.CapturedHeader}} will receive a sub-tree if that is what was last written; passing the same token to a string-only context (a header name, a literal in a subscription expression) coerces to text.


Anywhere a string can be typed in the platform - subscription expressions, map bindings, custom pipeline component property values, custom function arguments, port adapter settings - you can reference a variable with a double-curly-brace token. The exception is naming an object itself: the Name field on a variable, port, message type, schema, etc. is a literal, never an expression.

TokenResolves to
{{Variable.Name}}The current value of the named variable in this application. Resolves to a scalar, list, sub-tree, or whole message depending on what was last written into the slot.

The token grammar is identical to {{Promoted.Name}} and {{Message.MessageType}} - the same expression engine evaluates all three. A subscription expression that filters a send port to only pick up messages while a feature flag is on, for example, looks like this:

EXPRESSIONsubscription on a send port
{{Message.MessageType}} == "Invoice" && {{Variable.InvoiceForwardingEnabled}} == "true"

A custom pipeline component can also write to a variable directly - setting {{Variable.Name}} from C# code - which is the canonical way to surface a derived value out of a pipeline for the rest of the flow to consume. See Custom pipeline components.


Declaring a variable allocates the slot. Giving it a value is the job of port variable assignments - an optional stage that runs immediately after the port has been entered and immediately before it is left. Every port type exposes the same two assignment points:

StageRunsTypical use
On EntryImmediately after the message enters the port, before the pipeline / map stages.Capture values from the incoming message or its context - headers, query parameters, file name, sender identity, the inbound payload itself - so downstream stages can reference them.
On ExitImmediately before the message leaves the port, after the pipeline / map stages have run.Capture values from the transformed message that routing logic, send ports later in the flow, or activity tracking will need.

On a receive port the On Entry stage is stage 2 and On Exit is stage 5 in the five-stage flow (Adapter is stage 1). On a send port the order is mirrored - On Entry is stage 1, On Exit is stage 4, and Adapter is the final stage 5. A two-way port runs both stages on the request and again on the response, mirrored. The numbered stage diagrams in the Receive, Send, Loopback, and Null port articles show exactly where the variable stage sits for each port type and direction.

Variables are the only stage every port type has in common. Loopback and null ports do not run a pipeline or a map and have no adapter, but they still have On Entry and On Exit variable assignments - useful even when no other transformation is configured.

Variables can also be written outside the port assignment stages: a custom pipeline component setting {{Variable.Name}} from code, or an XSLT map writing back through the platform's helper functions, will mutate the same slot. The port assignment stages are simply the configuration-only path that does not require code.


Three patterns account for most of how variables are used in practice. The platform does not enforce any of these - a variable is just a slot - but the patterns are worth recognizing because they show up across most non-trivial integrations.

Pattern 1 - Loop counter. A custom pipeline component that splits a multi-record file into individual messages can use a variable as the running index, incrementing it on each iteration so downstream stages know which record they are looking at.

EXPRESSIONin a pipeline component / map binding
{{Variable.RecordIndex}}

The component reads the current value, uses it, increments it, writes it back. Because variables are application-scoped, the index is visible to every stage that runs while the loop is in flight - no need to thread the value through component arguments.

Pattern 2 - XPath capture. A common need is to lift a value out of the inbound message and reference it later in the flow, without making it part of the message type as a promotion. An On Entry variable assignment using an XPath does exactly that:

XPATHport On Entry assignment
Variable.CorrelationId  ←  /Order/Header/CorrelationId/text()

From that point on, every stage in the application can reach {{Variable.CorrelationId}} - a send port can use it in a subscription expression, a map can drop it into the response, a custom pipeline component can log it. The value is a scalar string, because that is what the XPath returned.

Pattern 3 - Holding a fragment or a whole message. The same XPath assignment works for sub-trees and whole messages. To capture an entire envelope inbound for later wrapping:

XPATHport On Entry assignment
Variable.OriginalRequest  ←  /

The variable now holds the whole message as a sub-tree. A two-way send port further along can drop {{Variable.OriginalRequest}} into an outbound envelope produced by a map, embedding the original payload verbatim. The same technique with a more selective XPath - /Order/ShipTo, say - captures just that branch.


Variables are evaluated lazily. The runtime does not pre-resolve every variable reference at the start of a flow; it resolves each {{Variable.Name}} token at the moment the stage that contains it executes.

StageWhat happens
Slot allocationWhen the application starts, every variable defined in it has a slot. Until something writes to a slot, reading it returns an empty value.
AssignmentA port On Entry / On Exit stage, a custom pipeline component, or a map writes a value. The slot's runtime type becomes whatever was just written.
Reference resolutionAn expression containing {{Variable.Name}} is evaluated - the slot's current value is substituted in, with shape coercion if the consuming context demands a string.

Because the slot is application-scoped, two messages flowing through the same application at the same time share it. Treat a variable as state owned by the integration, not state owned by the message - if per-message state is what you need, a promotion is the right tool.



That is the whole job

Define a slot under the application, fill it from a port stage or a pipeline component, reference it as {{Variable.Name}} wherever you need it. Untyped, application-scoped, shared by every message in flight.