shashank created CAMEL-25678:
--------------------------------

             Summary: camel-core - Switch EIP case values and the Dynamic 
Router uriDelimiter do not resolve property placeholders, so a case value with 
a placeholder never matches and the slip is not split
                 Key: CAMEL-25678
                 URL: https://issues.apache.org/jira/browse/CAMEL-25678
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank
            Assignee: shashank


Every String option of an EIP is passed through property-placeholder resolution 
when the route is created ({{AbstractReifier.parseString}}); this is also how 
route template and Kamelet parameters reach an EIP. Two options are used as 
written instead.

h3. 1. Switch EIP: the case value

{{SwitchReifier.createProcessor}} (main cd2ce899764e, lines 51-56) builds the 
dispatch table from {{c.getValue().toLowerCase(Locale.ENGLISH)}}. The {{uri}} 
of the case and of {{otherwise}} goes through {{staticUri}}, which calls 
{{parseString}}, but the value does not. A case such as

{code:java}
from("direct:tickets")
    .doSwitch(header("tier"))
        .doCase("{{gold.code}}", "direct:gold")
        .otherwise("direct:standard");
{code}

is keyed by the text \{\{gold.code\}\}. No selector result matches it (only a 
header that literally contains the text \{\{gold.code\}\} does), so every 
message goes to {{otherwise}}, or continues after the Switch without a 
destination when there is no {{otherwise}}. Nothing is logged. The same happens 
in XML and YAML, and for a route template or Kamelet parameter used as a case 
value (doCase("\{\{tier\}\}", ...) in a {{routeTemplate}}), where a placeholder 
is the only way to pass the parameter. The duplicate check also compares the 
unresolved text, so two cases that resolve to the same value are not rejected 
at startup as the documentation says.

The Switch documentation says that case and fallback URIs support property 
placeholders and that values are "literal"; in the design (CAMEL-24988) 
"literal" is used in contrast to patterns and wildcards. It does not say that 
values do not support placeholders.

h3. 2. Dynamic Router EIP: uriDelimiter

{{DynamicRouterReifier.createProcessor}} (lines 36-37) uses 
{{definition.getUriDelimiter()}} as written. {{RoutingSlipReifier}} (line 38) 
and {{RecipientListReifier}} (line 56) resolve their delimiter with 
{{parseString}}. With uriDelimiter("\{\{delim\}\}") and a slip 
{{"mock:a;mock:b"}}, the slip is not split and the exchange is sent to one 
endpoint whose uri is the whole slip (for {{mock:}}, a mock named 
{{a;mock:b}}); with other components it fails or sends to an unexpected 
endpoint.

h3. Reproduction

New tests in camel-core; on main:
* {{SwitchEndpointTest.caseValueResolvesPropertyPlaceholder}}: 
doCase("\{\{switch.tier\}\}", "mock:gold") with {{switch.tier=gold}}, headers 
{{gold}}, {{GOLD}} and the literal text \{\{switch.tier\}\}: {{mock://gold Body 
of message: 0. Expected: <first> but was: <third>}} (only the literal 
placeholder text matched).
* {{SwitchEndpointTest.caseValueResolvesRouteTemplateParameter}}: a route 
template with doCase("\{\{tier\}\}", "mock:gold") and {{tier=silver}}: 
{{mock://gold Body of message: 0. Expected: <first> but was: <second>}}.
* {{SwitchValidationTest.rejectsDuplicateValuesAfterResolvingPlaceholders}}: 
doCase("\{\{switch.tier\}\}", ...) and {{doCase("gold", ...)}} start without 
error: {{Expected java.lang.Exception to be thrown, but nothing was thrown.}}
* {{DynamicRouterUriDelimiterPlaceholderTest}}: a slip {{mock:a;mock:b}} with 
uriDelimiter("\{\{slip.delimiter\}\}") and {{slip.delimiter=;}}: {{mock://a 
Received message count. Expected: <1> but was: <0>}}.

h3. Proposed fix

Resolve both with {{parseString}} in the reifier: the case value before it is 
lower-cased (and before the duplicate check), the {{uriDelimiter}} before the 
default is applied. Values without placeholders are unchanged. A placeholder of 
a property that does not exist then fails the route at startup, as it does for 
the other options.

Not changed, same pattern, smaller impact (follow-ups if wanted): 
{{RollbackReifier}} uses {{message}} as written (only the text of the 
exception), and {{TransformDataTypeReifier}} uses {{fromType}}/{{toType}} as 
written (fails at startup with "No transformer").

How it was found: a Lean 4 model of the Switch dispatch table (case value, 
placeholder resolution, case folding) against the Switch documentation: on main 
no selector result can select a case whose value contains a placeholder, and 
with the value resolved before folding the table matches the documentation for 
every value; the counterexample is checked by {{decide}}. A follow-up sweep of 
all reifiers for String options that are not passed to {{parseString}} found 
the Dynamic Router delimiter. The model is a sketch; the tests are the evidence.

Affected: main (cd2ce899764e; tested). Switch: camel-4.23.x (same line 54, 
read, not run); the Switch EIP is not on camel-4.22.x and older. Dynamic 
Router: camel-4.23.x, camel-4.22.x, camel-4.18.x and camel-4.14.x have the same 
lines 36-37 (read, not run).

Duplicate check (2026-10-15): JIRA text "uriDelimiter" (only CAMEL-25083, 
iterating a delimiter-only string), "switch case value placeholder", 
"dynamicRouter placeholder", summaries with "placeholder" since June 2026 
(CAMEL-24414, CAMEL-24282, CAMEL-24696 and others, none about these options); 
GitHub pull requests "Switch EIP", "uriDelimiter", "SwitchReifier" (nothing 
open or related).

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to