Raymond created CAMEL-25922:
-------------------------------
Summary: Route template parameter value containing }} (e.g. JSON)
fails with “Missing {{ from the text”
Key: CAMEL-25922
URL: https://issues.apache.org/jira/browse/CAMEL-25922
Project: Camel
Issue Type: Bug
Affects Versions: 4.23.0
Reporter: Raymond
h3. Problem
When a route template parameter value contains the text {{}}}} (for example a
JSON body or a Simple expression that ends with a nested object), creating the
route fails with:
{noformat}
org.apache.camel.FailedToCreateRouteException: Failed to create route:
templated at: >>> SetBody[simple{{{expression}}}] <<< ...
Caused by: java.lang.IllegalArgumentException: Missing {{ from the text:
{"title":${header.title}}
at
org.apache.camel.component.properties.DefaultPropertiesParser$ParsingContext.readProperty(DefaultPropertiesParser.java:268)
at
org.apache.camel.component.properties.DefaultPropertiesParser$ParsingContext.doParseNested(DefaultPropertiesParser.java:192)
at
org.apache.camel.component.properties.DefaultPropertiesParser$ParsingContext.doParseNested(DefaultPropertiesParser.java:217)
...
{noformat}
The exact same expression works when used directly in a route. It only fails
when passed as a template parameter (or any property value referenced via
{{...}}).
The cause: {{{{expression}}}} is resolved, and then the resolved value is
parsed again for nested placeholders. In {{{"title":${header.title}}}} the
closing brace of {{${...}}} plus the closing brace of the JSON object form
{{}}}}, which is taken as a placeholder suffix without a matching prefix.
Top-level text without {{{{}} is skipped by the parser, so it is not affected.
A resolved value is always parsed, so there the same text fails.
h3. Reproducer
{code:java}
import java.util.Map;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class JsonTemplateParameterIssue {
public static void main(String[] args) throws Exception {
try (DefaultCamelContext context = new DefaultCamelContext()) {
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
// works: expression used directly
from("direct:plain")
.setBody().simple("{\"title\":${header.title}}");
// fails: same expression passed as a route template
parameter
routeTemplate("setBodyTemplate")
.templateParameter("expression")
.from("direct:{{routeId}}")
.setBody().simple("{{expression}}");
}
});
context.start();
System.out.println(context.createProducerTemplate()
.requestBodyAndHeader("direct:plain", null, "title",
"\"Hello\""));
// prints {"title":"Hello"}
context.addRouteFromTemplate("templated", "setBodyTemplate",
Map.of("routeId", "templated", "expression",
"{\"title\":${header.title}}"));
// throws FailedToCreateRouteException
// Caused by: IllegalArgumentException: Missing {{ from the text:
{"title":${header.title}}
}
}
}
{code}
h3. Observed results (Camel 4.23.0, template parameter "expression")
||Value||Result||
|{{{"title":${header.title}}}}|FAIL - Missing {{|
|{{{"a":{"b":1}}}} (plain nested JSON, constant)|FAIL - Missing {{|
|{{{"title":"${header.title}"}}}|OK|
|{{{"title":${header.title} }}} (space added)|OK|
|{{{"title":${header.title\}}}} (escaped)|OK|
|any of the above with {{setNestedPlaceholder(false)}}|OK|
A related silent case: a value such as {{{{"a":1}}}} does not fail. It is
resolved as placeholder key {{"a"}} with default value {{1}}, so the route gets
{{1}} instead of the JSON.
h3. Context
We hit this in Assimbly (https://github.com/assimbly/runtime), which builds
routes from Kamelet-style route templates. User-supplied setBody expressions
(often JSON) are passed as template parameters. Users can't be expected to
escape {{}}}} in JSON payloads, and turning off nested placeholders for the
whole CamelContext is too broad.
h3. Suggested improvements (any of these)
# When parsing a resolved (nested) value, treat an unmatched {{}}}} (no {{{{}}
in the text) as literal text instead of throwing. This is consistent with
top-level text, which is left untouched when it contains no {{{{}}.
# Allow route template parameters to be marked as raw/literal (e.g. per
{{templateParameter}}, or a template-level option), so their values are not
parsed for nested placeholders.
# Document the {{\}}}} / {{\{{}} escape for template parameter values in the
route template docs.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)