{"id":2100,"date":"2026-02-04T23:32:39","date_gmt":"2026-02-04T10:32:39","guid":{"rendered":"https:\/\/www.ronella.xyz\/?p=2100"},"modified":"2026-02-04T23:32:39","modified_gmt":"2026-02-04T10:32:39","slug":"apache-camel-variables-and-variablereceive-in-java-dsl","status":"publish","type":"post","link":"https:\/\/www.ronella.xyz\/?p=2100","title":{"rendered":"Apache Camel Variables and VariableReceive in Java DSL"},"content":{"rendered":"<p>In Camel\u2019s Java DSL, a <strong>variable<\/strong> is a key\u2013value pair you associate with an <code>Exchange<\/code>, a route, or the <code>CamelContext<\/code>, and <strong>VariableReceive<\/strong> is a mode where incoming data is written into variables instead of the <code>Message<\/code> body and headers. Used properly, these give you a clean, explicit way to manage routing state without polluting payloads or headers.<\/p>\n<hr \/>\n<h2>1. Variables in Java DSL: the mental model<\/h2>\n<ul>\n<li>A variable is a named value stored on the <strong>exchange<\/strong>, <strong>route<\/strong>, or <strong>CamelContext<\/strong>, accessed with <code>setVariable<\/code> \/ <code>getVariable<\/code> or via EIPs.<\/li>\n<li>Variables are separate from the message body, headers, and legacy exchange properties, and are meant as a <strong>scratchpad<\/strong> for state you need while routing.<\/li>\n<\/ul>\n<p>Conceptually, you choose the scope based on <em>who<\/em> should see the value and <em>how long<\/em> it should live.<\/p>\n<hr \/>\n<h2>2. Local, global, and route\u2011scoped variables<\/h2>\n<h2>2.1 Exchange\u2011local variable<\/h2>\n<p>Exchange\u2011local variables live only during processing of a single message.<\/p>\n<pre><code class=\"language-java\">from(&quot;direct:local-variable&quot;)\n    .process(exchange -&gt; {\n        \/\/ store some state for this exchange only\n        exchange.setVariable(&quot;customerId&quot;, 12345L);\n    })\n    .process(exchange -&gt; {\n        Long id = exchange.getVariable(&quot;customerId&quot;, Long.class);\n        System.out.println(&quot;Customer ID = &quot; + id);\n    });<\/code><\/pre>\n<p><strong>Rationale:<\/strong> use exchange\u2011local variables for state that is logically tied to a single message and must not leak to other messages or routes.<\/p>\n<hr \/>\n<h2>2.2 Global and route\u2011scoped variables<\/h2>\n<p>Global variables live in the <code>CamelContext<\/code>, and route\u2011scoped variables are global variables whose keys include a route identifier.<\/p>\n<pre><code class=\"language-java\">from(&quot;direct:global-and-route&quot;)\n    .routeId(&quot;routeA&quot;)\n    .process(exchange -&gt; {\n        CamelContext context = exchange.getContext();\n\n        \/\/ global variable (shared across all routes)\n        context.setVariable(&quot;globalFlag&quot;, true);\n\n        \/\/ route\u2011scoped variable using naming convention\n        exchange.setVariable(&quot;route:routeA:maxRetries&quot;, 3);\n    })\n    .process(exchange -&gt; {\n        CamelContext context = exchange.getContext();\n\n        Boolean globalFlag =\n            context.getVariable(&quot;globalFlag&quot;, Boolean.class);\n\n        Integer maxRetries =\n            context.getVariable(&quot;route:routeA:maxRetries&quot;, Integer.class);\n\n        System.out.println(&quot;Global flag   = &quot; + globalFlag);\n        System.out.println(&quot;Max retries A = &quot; + maxRetries);\n    });<\/code><\/pre>\n<p>You can also read a global variable from an exchange with the <code>global:<\/code> prefix (if you prefer that naming scheme).<\/p>\n<p><strong>Rationale:<\/strong><\/p>\n<ul>\n<li>Global variables are convenient for application\u2011level flags or configuration computed at runtime.<\/li>\n<li>Route\u2011scoped variables avoid collisions when different routes use similar semantic names like <code>maxRetries<\/code> or <code>threshold<\/code>.<\/li>\n<\/ul>\n<hr \/>\n<h2>3. SetVariable and SetVariables EIPs in Java DSL<\/h2>\n<p>While you can always call <code>exchange.setVariable(...)<\/code> directly, the Java DSL provides EIPs that make variable usage declarative in your routes.<\/p>\n<h2>3.1 Set a single variable<\/h2>\n<pre><code class=\"language-java\">from(&quot;direct:set-variables&quot;)\n    \/\/ single variable from constant\n    .setVariable(&quot;status&quot;, constant(&quot;NEW&quot;))\n\n    \/\/ multiple variables in one go\n    .setVariables(\n        &quot;randomNumber&quot;, simple(&quot;${random(1,100)}&quot;),\n        &quot;copyOfBody&quot;,  body()\n    )\n\n    .process(exchange -&gt; {\n        String status  = exchange.getVariable(&quot;status&quot;, String.class);\n        Integer random = exchange.getVariable(&quot;randomNumber&quot;, Integer.class);\n        String bodyCopy = exchange.getVariable(&quot;copyOfBody&quot;, String.class);\n\n        System.out.printf(\n            &quot;status=%s, random=%d, copy=%s%n&quot;,\n            status, random, bodyCopy\n        );\n    });<\/code><\/pre>\n<p><strong>Rationale:<\/strong> <code>setVariable<\/code> and <code>setVariables<\/code> keep the \u201cI am defining variables here\u201d logic inside the DSL layer rather than hiding it in processors, which makes routes easier to read and reason about.<\/p>\n<hr \/>\n<h2>4. VariableReceive on the consumer: fromV<\/h2>\n<p><strong>VariableReceive<\/strong> changes how Camel <strong>receives<\/strong> data from endpoints: instead of populating the message body and headers, Camel stores them into variables.<\/p>\n<p>Using it on the consumer side is done with <code>fromV<\/code> (the Java DSL variant of <code>from<\/code>):<\/p>\n<pre><code class=\"language-java\">fromV(&quot;direct:incoming&quot;, &quot;originalBody&quot;)\n    \/\/ body is empty here because the incoming payload is stored in variable &quot;originalBody&quot;\n    .transform().simple(&quot;Processed: ${body}&quot;) \/\/ -&gt; &quot;Processed: &quot;\n    .process(exchange -&gt; {\n        String original =\n            exchange.getVariable(&quot;originalBody&quot;, String.class);\n        String processed =\n            exchange.getMessage().getBody(String.class);\n\n        System.out.println(&quot;Original  = &quot; + original);\n        System.out.println(&quot;Processed = &quot; + processed);\n    });<\/code><\/pre>\n<p>What actually happens:<\/p>\n<ul>\n<li>On entry, Camel stores the incoming body in variable <code>originalBody<\/code>.<\/li>\n<li>The <code>Message<\/code> body is empty (or at least not the original payload), so <code>${body}<\/code> in the transform step produces <code>&quot;Processed: &quot;<\/code>.<\/li>\n<li>Later, you can inspect or restore the original payload by reading the <code>originalBody<\/code> variable.<\/li>\n<\/ul>\n<p><strong>Rationale:<\/strong> this is useful when you want to separate <em>transport reception<\/em> from <em>business payload<\/em>, or when you want to ensure that early processors do not accidentally work on the raw incoming data.<\/p>\n<hr \/>\n<h2>5. VariableReceive on the producer: toV<\/h2>\n<p>You can apply VariableReceive to producer calls with <code>toV<\/code>, which works like <code>to<\/code> but puts the response into a variable instead of overwriting the message body.<\/p>\n<pre><code class=\"language-java\">from(&quot;direct:call-service&quot;)\n    \/\/ main message body is here\n    .toV(&quot;http:\/\/localhost:8080\/service&quot;, null, &quot;serviceReply&quot;)\n    .process(exchange -&gt; {\n        \/\/ original body is preserved\n        String original =\n            exchange.getMessage().getBody(String.class);\n\n        \/\/ HTTP response body from the service\n        String reply =\n            exchange.getVariable(&quot;serviceReply&quot;, String.class);\n\n        \/\/ response header X-Level as header variable\n        String level =\n            exchange.getVariable(&quot;header:serviceReply.X-Level&quot;, String.class);\n\n        String combined = &quot;Original=&quot; + original\n                        + &quot;, Reply=&quot; + reply\n                        + &quot;, Level=&quot; + level;\n\n        exchange.getMessage().setBody(combined);\n        System.out.println(combined);\n    });<\/code><\/pre>\n<p>Semantics:<\/p>\n<ul>\n<li>Request is sent to <code>http:\/\/localhost:8080\/service<\/code> using the current body and headers.<\/li>\n<li>Response body is placed into variable <code>serviceReply<\/code>.<\/li>\n<li>Response headers are placed into variables named <code>header:serviceReply.&lt;HeaderName&gt;<\/code>.[<a href=\"https:\/\/camel.apache.org\/manual\/variables.html\">camel.apache<\/a>]<\/li>\n<li>The exchange\u2019s message body and headers after <code>toV<\/code> remain what they were before the call.<\/li>\n<\/ul>\n<p><strong>Rationale:<\/strong> this is an <strong>enrichment<\/strong>\u2011style pattern where you treat external responses as additional data points rather than as replacements for your main message.<\/p>\n<hr \/>\n<h2>6. Comparing body, headers, properties, and variables<\/h2>\n<p>For Java DSL routes you\u2019ll typically use all four concepts; the key is to pick the right tool for the job.<\/p>\n<table>\n<thead>\n<tr>\n<th>Kind<\/th>\n<th>Scope \/ lifetime<\/th>\n<th>Java access (simplified)<\/th>\n<th>Typical use<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Body<\/td>\n<td>Current message only<\/td>\n<td><code>exchange.getMessage().getBody()<\/code><\/td>\n<td>Business payload<\/td>\n<\/tr>\n<tr>\n<td>Header<\/td>\n<td>Current message only<\/td>\n<td><code>exchange.getMessage().getHeader(&quot;X&quot;, T.class)<\/code><\/td>\n<td>Protocol \/ metadata (HTTP, JMS, etc.)<\/td>\n<\/tr>\n<tr>\n<td>Property<\/td>\n<td>Current exchange (legacy pattern)<\/td>\n<td><code>exchange.getProperty(&quot;key&quot;, T.class)<\/code><\/td>\n<td>Cross\u2011processor flags, routing metadata<\/td>\n<\/tr>\n<tr>\n<td>Variable<\/td>\n<td>Exchange \/ route \/ global repositories<\/td>\n<td><code>exchange.getVariable(&quot;key&quot;, T.class)<\/code> or context API<\/td>\n<td>Extra state, external responses, config\u2011like<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Rationale:<\/strong> using variables for scratchpad and enrichment state avoids overloading headers or properties with routing concerns, which improves maintainability and refactorability.<\/p>\n<hr \/>\n<h2>7. End\u2011to\u2011end Java program example (using only direct:)<\/h2>\n<p>This example shows how variables and <code>toV<\/code>\/<code>from<\/code> can work together <strong>without<\/strong> any external HTTP endpoint. We simulate a \u201cdiscount service\u201d with another <code>direct:<\/code> route.<\/p>\n<pre><code class=\"language-java\">import org.apache.camel.CamelContext;\nimport org.apache.camel.builder.RouteBuilder;\nimport org.apache.camel.impl.DefaultCamelContext;\n\nvoid main() throws Exception {\n    CamelContext context = new DefaultCamelContext();\n\n    context.addRoutes(new RouteBuilder() {\n        @Override\n        public void configure() {\n\n            \/\/ 1) &quot;Discount service&quot; route, purely in-memory\n            from(&quot;direct:discountService&quot;)\n                .process(exchange -&gt; {\n                    String originalOrder =\n                        exchange.getMessage().getBody(String.class);\n\n                    \/\/ pretend we call an external system and compute a discount\n                    String discountReply =\n                        &quot;discount=10% for order &quot; + originalOrder;\n\n                    exchange.getMessage().setBody(discountReply);\n                });\n\n            \/\/ 2) Main route using variables + VariableReceive with toV\n            from(&quot;direct:start&quot;)\n                \/\/ keep the original order body in a variable\n                .setVariable(&quot;originalOrder&quot;, body())\n\n                \/\/ call the in-memory discountService; response -&gt; variable &quot;discountReply&quot;\n                .toV(&quot;direct:discountService&quot;, null, &quot;discountReply&quot;)\n\n                \/\/ build combined response\n                .process(exchange -&gt; {\n                    String originalOrder =\n                        exchange.getVariable(&quot;originalOrder&quot;, String.class);\n                    String discountReply =\n                        exchange.getVariable(&quot;discountReply&quot;, String.class);\n\n                    String result = &quot;Order=&quot; + originalOrder\n                                  + &quot;, DiscountService=&quot; + discountReply;\n\n                    exchange.getMessage().setBody(result);\n                    System.out.println(result);\n                });\n        }\n    });\n\n    context.start();\n\n    \/\/ Send a test order into the main route\n    context.createProducerTemplate()\n           .sendBody(&quot;direct:start&quot;, &quot;{\\&quot;id\\&quot;:1,\\&quot;total\\&quot;:100}&quot;);\n\n    Thread.sleep(2000);\n    context.stop();\n}\n<\/code><\/pre>\n<ul>\n<li>The <code>direct:discountService<\/code> route stands in for an external dependency but uses only the in\u2011memory <strong>direct<\/strong> component.<\/li>\n<li><code>toV(&quot;direct:discountService&quot;, null, &quot;discountReply&quot;)<\/code> still demonstrates VariableReceive on a producer: the reply body goes into the <code>discountReply<\/code> variable; the main message body is preserved.<\/li>\n<li>We then combine <code>originalOrder<\/code> and <code>discountReply<\/code> explicitly in the final processor, making the variable usage and flow of data very clear.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In Camel\u2019s Java DSL, a variable is a key\u2013value pair you associate with an Exchange, a route, or the CamelContext, and VariableReceive is a mode where incoming data is written into variables instead of the Message body and headers. Used properly, these give you a clean, explicit way to manage routing state without polluting payloads [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[92],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2100"}],"collection":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2100"}],"version-history":[{"count":1,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2100\/revisions"}],"predecessor-version":[{"id":2101,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2100\/revisions\/2101"}],"wp:attachment":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2100"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2100"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}