blob: b06d0b74c0313f3a3e6efdd5b07b2c95d19b593f [file] [log] [blame]
= Variables
*Available from Camel 4.4*
In Camel 4.4, we have introduced the concept of _variables_.
A variable is a key/value that can hold a value that can either be private per `Exchange`,
or shared per route, or per `CamelContext`.
NOTE: You can also use _exchange properties_ as variables, but the exchange properties are also used internally by Camel,
and some EIPs and components. With the newly introduced _variables_ then these are exclusively for end users.
== Variable Repository
The variables are stored in one or more `org.apache.camel.spi.VariableRepository`. By default, there are the following repositories
- `ExchangeVariableRepository` - A private repository per `Exchange` that holds variables that are private for the lifecycle of each `Exchange`.
- `RouteVariableRepository` - Uses `route` as id. A single repository, that holds variables per `Route`.
- `GlobalVariableRepository` - Uses `global` as id. A single global repository for the entire `CamelContext`.
The `ExchangeVariableRepository` is special as its private per exchange and is the default repository when used during routing.
The `RouteVariableRepository` is a single repository that holds variables that are route scoped.
TIP: There is also `org.apache.camel.spi.BrowsableVariableRepository` which is an extension to `VariableRepository` that
has APIs to browse the current variables. Camel uses this with Camel JBang, and JMX to be able to see the current variables
from management tooling, CLI, and the developer console.
=== Custom variable repositories
You can implement custom `org.apache.camel.spi.VariableRepository` and plugin to be used out of the box with Apache Camel.
For example, you can build a custom repository that stores the variables in a database, so they are persisted.
Each repository must have its own unique id. However, it's also possible to replace the default `global`, or `route` repositories with another.
IMPORTANT: The id `exchange` and `header` is reserved by Camel internally and should not be used as id for custom repositories.
== Getting and setting variables from Java API
To get a local variable from the current exchange, you can do this via Java API:
[source,java]
----
String myValue = "...";
exchange.setVariable("myKey", myValue);
// and later to get the variable
Object val = exchange.getVariable("myKey");
// you can get the value as a specific type
String str = exchange.getVariable("myKey", String.class);
----
The API on `Exchange` will by default get the variables from its local private repository.
However, you can also get variables from other repositories, such as the `global` as show:
[source,java]
----
Object val = exchange.getVariable("global:myGlobalKey");
----
And you can also assign a global variable by prefixing with `global:` as follows:
[source,java]
----
exchange.setVariable("global:myGlobalKey", someObjectHere);
----
There is also API on `CamelContext` to get variables. However, this API will by default get from the `global` repository,
as it's not possible to get variables from any inflight `Exchange` currently being routed.
[source,java]
----
Object val = context.getVariable("myGlobalKey");
// you can get the value as a specific type
String str = context.getVariable("myGlobalKey", String.class);
----
You can also assign a variable to a specific route with `route:` as follows:
[source,java]
----
exchange.setVariable("route:myRouteId:myRouteKey", someObjectHere);
----
And you can get route variables as well:
[source,java]
----
Object val = context.getVariable("route:myRouteId:myRouteKey");
// you can get the value as a specific type
String str = context.getVariable("route:myRouteId:myRouteKey", String.class);
----
== Setting and getting variables from DSL
It is also possible to use variables in Camel xref:routes.adoc[routes] using the
setVariable, removeVariable, and convertVariableTo EIPs.
These EIPs make it possible to set and remove variables from routes. And you can also access variables from the xref:components:languages:simple-language.adoc[Simple] language.
In the following route, we set a variable on the exchange which we use later to build a human-readable event message:
[tabs]
====
Java::
+
[source,java]
----
from("kafka:order.in")
.setVariable("customerId", jq(".customer.custId"))
.setVariable("country", jq(".customer.address.co"))
.transform().simple("Order received from customer ${variable.customerId} in ${variable.country}")
.to("kafka:order.event");
----
XML::
+
[source,xml]
----
<route>
<from uri="kafka:order.in"/>
<setVariable name="customerId">
<jq>.customer.custId</jq>
</setVariable>
<setVariable name="country">
<jq>.customer.address.co</jq>
</setVariable>
<transform>
<simple>Order received from customer ${variable.customerId} in ${variable.country}</simple>
</transform>
<to uri="kafka:order.event"/>
</route>
----
YAML::
+
[source,yaml]
----
- route:
from:
uri: kafka:order.in
steps:
- setVariable:
name: customerId
jq:
expression: .customer.custId
- setVariable:
name: country
jq:
expression: .customer.address.co
- transform:
simple:
expression: "Order received from customer ${variable.customerId} in ${variable.country}"
- to:
uri: kafka:order.event
----
====
When `route` variables in Camel routes, then the `routeId` is implied as the current route, if not explicit declared.
For example, the following example the first route will set a variable (`route:second:foo`) in the second route.
Then the second route can get hold of the variable without having to specify its route id `route:foo`:
[tabs]
====
Java::
+
[source,java]
----
from("direct:start").routeId("first")
// sets variable in another route
.setVariable("route:second:foo").constant("Hello World")
.to("mock:end");
from("direct:second").routeId("second")
// use variable from this route
.setBody().variable("route:foo");
----
====
== Configuring initial variables on startup
When Camel is starting then it's possible to configure initial variables for `global` and `route` repositories only.
This can be done in `application.properties` as shown below:
[source,properties]
----
camel.variable.greeting = Random number
camel.variable.random = 999
----
The variables are default set on the `global` repository, but you can set route scoped variables,
using `route.` as prefix. As we cannot use colon (`:`) in property keys, then dot is used to separate
the route id from the variable name, eg `myRoute.gold`.
[source,properties]
----
camel.variable.route.myRoute.gold = true
camel.variable.greeting = Random number
camel.variable.random = 999
----
Here the gold variable is set on the `route` repository, and the other variables are set on the `global` repository.
The value of a variable can also be loaded from the file system, such as a JSon file. To do this, you should
prefix the value with `resource:file:` or `resource:classpath:` to load from the file system or classpath,
as shown below:
[source,properties]
----
camel.variable.user-template = resource:file:/etc/user.json
----
Camel (*Camel 4.8*) will automatically convert the value to appropriate type:
- all digits are converted to an int or long
- true/false are converted to a boolean
- otherwise string value
There is also support for referring to other existing beans, using the `#bean:` syntax:
[source,properties]
----
camel.variable.cheese = #bean:myCheeseBean
----
Or create a new bean via the `#class:` or `#type:` syntax:
[source,properties]
----
camel.variable.cheese = #class:com.foo.MyClassName
----
Or if the value must be of a special type, you can specify this via `#valueAs` as follows:
[source,properties]
----
camel.variable.amount = #valueAs(float):1.23
----
== Using Variables with EIPs
The following commonly used EIPs for sending and receiving, and transforming messages, have
special support for choosing to use variables over the current `Exchange`:
- from
- to
- toD
- enrich
- poll
- pollEnrich
- wireTap
- unmarshal
- marshal
The intention is to make it more convenient and easy to _gather data_ from other systems without any ceremony to keep
existing data by using techniques such as storing the data temporary using headers, exchange properties,
or with the xref:components:eips:claimCheck-eip.adoc[Claim Check] EIP.
=== Important concept when using variables and EIPs
It is **important** to understand that the variables focus the use of the message **body** only.
This is on purpose to keep it simpler and primary work with the message body as the user data.
The following table summarizes what the EIP supports with variables:
|===
|*EIP* | *VariableSend* | *VariableReceive*
| From | | yes
| To | yes | yes
| ToD | yes | yes
| Enrich | yes | yes
| Poll | | yes
| PollEnrich | | yes
| WireTap | yes |
| Unmarshal | yes | yes
| Marshal | yes | yes
|===
The EIPs listed above have support for using variables when sending and receiving data. This is done by using the `variableSend` and `variableReceive` options
to specify the name of the variable.
The EIPs works in two modes where *variableSend* and *variableReceive* are a little bit different, so pay attention to the following table:
|===
| *VariableSend* | *VariableReceive*
| *Sending Headers:* Message | *Received Headers:* Variable
| *Sending Body:* Variable | *Received Body:* Variable
|===
The *VariableSend* is intended for sending as regular Camel where the sending headers are from the current `Message` and the body is
from the variable. In other words it's only the message body taken from the variable instead of the current `Message` body.
The *VariableReceive* works in a different mode. The idea is that all the received data is stored as variables. This means the current `Message`
is not changed at all. The received body is stored in the variable, and the received headers (transport headers etc.) are stored as read-only
headers as variables as well. The names of the variable is `header:variableName.headerName`. For example, if the variable is `myVar` and the header is `Content-Type`
then the header is stored as a variable with the full name `header:myVar.Content-Type`.
=== Example using Variable Receive
When the EIP is using *VariableReceive*, then the `Message` on the `Exchange` is not in use, but the body and headers will be from the variable.
For example, given the following `Message` containing:
[source,properties]
----
header.foo=123
header.bar=456
body=Hello World
----
And a remote service is called via the route below, and this service returns a new header (`level`) and body: 'Bye World'
[tabs]
====
Java::
+
[source,java]
----
from("direct:service")
.to("http:myservice")
.to("log:after");
----
XML::
+
[source,xml]
----
<route>
<from uri="direct:service"/>
<to uri="http:myservice"/>
<to uri="log:after"/>
</route>
----
YAML::
+
[source,yaml]
----
from:
uri: "direct:service"
steps:
- to: "http:myservice"
- to: "log:after"
----
====
Calling this route, the `Message` is updated to following:
[source,properties]
----
header.foo=123
header.bar=456
header.level=gold
body=Bye World
----
However, if you use *VariableReceive=myVar* to store the returned data from calling the remote service into a variable, then
the result changes as follows:
[tabs]
====
Java::
+
[source,java]
----
from("direct:service")
.toV("http:myservice", null, "myVar")
.to("log:after");
----
XML::
+
[source,xml]
----
<route>
<from uri="direct:service"/>
<to uri="http:myservice" variableReceive="myVar"/>
<to uri="log:after"/>
</route>
----
YAML::
+
[source,yaml]
----
from:
uri: "direct:service"
steps:
- to:
uri: http:myservice
variableReceive: myVar
- to: "log:after"
----
====
The `Message` on the current `Exchange` is not changed:
[source,properties]
----
header.foo=123
header.bar=456
body=Hello World
----
And the variable contains all the data received from the remote HTTP service separated into two variables:
[source,properties]
----
myVar=Bye World
header:myVar.level=gold
----
IMPORTANT: Notice the headers are stored with the syntax `header:variable.key`. In the example above the variable name is `myVar`,
and the header key is `level`, which gives: `header:myVar.level`.
=== Using variable to store incoming message body
You can configure the `from` to store the message body into a variable, instead of the `Message`. This makes it easy to have quick access
to the original incoming message body via the variable. Notice that the body on the `Message` will be `null`.
The following example from a unit test shows how to do this. Notice how Java DSL uses `fromV` to make it possible to specify
the name of the variable. In XML and YAML DSL you specify this using the `variableReceive` parameter.
[tabs]
====
Java::
+
[source,java]
----
fromV("direct:start", "myKey")
.transform().simple("Bye ${body}")
.to("mock:foo")
.setBody(variable("myKey"))
.to("mock:result");
----
XML::
+
[source,xml]
----
<route>
<from uri="direct:start" variableReceive="myKey"/>
<transform>
<simple>Bye ${body}</simple>
</transform>
<to uri="mock:foo"/>
<setBody>
<variable>myKey</variable>
</setBody>
<to uri="mock:result"/>
</route>
----
YAML::
+
[source,yaml]
----
from:
uri: "direct:start"
variableReceive: "myKey"
steps:
- transform:
simple: "Bye ${body}"
- to: "mock:foo"
- setBody:
variable: "myKey"
- to: "mock:result"
----
====
NOTE: In the examples above the transform `Bye $\{body}` will result as `Bye&#160;` because the `Message` has no message body, as the incoming
message body is stored in the variable `myKey` instead.