This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new fef6d46931dd CAMEL-23799: Sort init-block substitution keys by length
to prevent prefix collision (#24121)
fef6d46931dd is described below
commit fef6d46931dd881cc543e5561e3711ee470d8e65
Author: Adriano Machado <[email protected]>
AuthorDate: Fri Jun 19 15:42:37 2026 -0400
CAMEL-23799: Sort init-block substitution keys by length to prevent prefix
collision (#24121)
---
.../language/simple/SimpleExpressionParser.java | 13 ++++-
.../language/simple/SimplePredicateParser.java | 9 ++-
.../simple/SimpleInitBlockOverlappingKeysTest.java | 67 ++++++++++++++++++++++
3 files changed, 84 insertions(+), 5 deletions(-)
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
index 018f2a1df742..8a9e0d0d5792 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
@@ -17,6 +17,7 @@
package org.apache.camel.language.simple;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -88,12 +89,18 @@ public class SimpleExpressionParser extends
BaseSimpleParser {
part = part.substring(1);
}
this.expression = part;
- // use $$key as local variable in the expression afterwards
- for (String key : initParser.getInitKeys()) {
+ // use $$key as local variable in the expression afterwards.
+ // Sort by descending length so a longer key (e.g. "$ab") is
replaced before any
+ // shorter prefix (e.g. "$a"), preventing "$ab" from becoming
"${variable.a}b".
+ List<String> sortedKeys = new
ArrayList<>(initParser.getInitKeys());
+
sortedKeys.sort(Comparator.comparingInt(String::length).reversed());
+ for (String key : sortedKeys) {
this.expression = this.expression.replace("$" + key,
"${variable." + key + "}");
}
// use $$key() as local function in the expression afterwards
- for (String key : initParser.getInitFunctions()) {
+ List<String> sortedFunctions = new
ArrayList<>(initParser.getInitFunctions());
+
sortedFunctions.sort(Comparator.comparingInt(String::length).reversed());
+ for (String key : sortedFunctions) {
// no-arg functions
this.expression = this.expression.replace("$" + key +
"()", "${function(" + key + ")}");
// arg functions
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
index d50831ce0529..c52cc491050d 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
@@ -19,6 +19,7 @@ package org.apache.camel.language.simple;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
@@ -99,8 +100,12 @@ public class SimplePredicateParser extends BaseSimpleParser
{
part = part.substring(1);
}
this.expression = part;
- // use $$key as local variable in the expression afterwards
- for (String key : initParser.getInitKeys()) {
+ // use $$key as local variable in the expression
afterwards.
+ // Sort by descending length so a longer key (e.g. "$ab")
is replaced before any
+ // shorter prefix (e.g. "$a"), preventing "$ab" from
becoming "${variable.a}b".
+ List<String> sortedKeys = new
ArrayList<>(initParser.getInitKeys());
+
sortedKeys.sort(Comparator.comparingInt(String::length).reversed());
+ for (String key : sortedKeys) {
this.expression = this.expression.replace("$" + key,
"${variable." + key + "}");
}
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockOverlappingKeysTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockOverlappingKeysTest.java
new file mode 100644
index 000000000000..facc266d1645
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleInitBlockOverlappingKeysTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.language.simple;
+
+import org.apache.camel.LanguageTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Regression tests for the init-block key substitution ordering bug (L2).
When one key is a prefix of another (e.g.
+ * "$n" and "$name"), the shorter key must not be replaced first, or "$name"
becomes "${variable.n}ame". The fix sorts
+ * keys by descending length before substitution.
+ */
+public class SimpleInitBlockOverlappingKeysTest extends LanguageTestSupport {
+
+ @Override
+ protected String getLanguageName() {
+ return "simple";
+ }
+
+ /**
+ * Two keys where the short key ("n") is a prefix of the long key
("name"). Without the fix, replacing "$n" first
+ * corrupts "$name" into "${variable.n}ame".
+ */
+ private static final String OVERLAPPING_KEYS = """
+ $init{
+ $n := 'Bob';
+ $name := 'Alice';
+ }init$
+ full=$name,short=$n
+ """;
+
+ /**
+ * Three-level overlap: "v", "val", "value". The longest must be replaced
first.
+ */
+ private static final String THREE_LEVEL_OVERLAP = """
+ $init{
+ $v := 'one';
+ $val := 'two';
+ $value := 'three';
+ }init$
+ $value,$val,$v
+ """;
+
+ @Test
+ public void testOverlappingKeysTwoLevels() throws Exception {
+ assertExpression(exchange, OVERLAPPING_KEYS, "full=Alice,short=Bob\n");
+ }
+
+ @Test
+ public void testOverlappingKeysThreeLevels() throws Exception {
+ assertExpression(exchange, THREE_LEVEL_OVERLAP, "three,two,one\n");
+ }
+}