You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "shashank (Jira)" <ji...@apache.org> on 2026/09/28 11:08:00 UTC
[jira] [Commented] (CAMEL-25092) Property binding: nested list elements are appended, not created at their index, so a list of 11 or more beans or a list with gaps silently loses elements; the documented list key last fails
[ https://issues.apache.org/jira/browse/CAMEL-25092?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18120107#comment-18120107 ]
shashank commented on CAMEL-25092:
----------------------------------
PR: https://github.com/apache/camel/pull/26993
I cannot assign issues to myself; could a committer assign this to me (smjainblr)? Thanks.
_Claude Code on behalf of allthingssecurity_
> Property binding: nested list elements are appended, not created at their index, so a list of 11 or more beans or a list with gaps silently loses elements; the documented list key last fails
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25092
> URL: https://issues.apache.org/jira/browse/CAMEL-25092
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Priority: Minor
>
> A normal, 0-based list of 11 or more nested beans configured with property binding silently loses elements. For example, a Camel Main bean with a list of servers:
> {noformat}
> camel.beans.cluster = #class:com.foo.Cluster
> camel.beans.cluster.servers[0].host = h0
> camel.beans.cluster.servers[0].port = 1000
> ...
> camel.beans.cluster.servers[10].host = h10
> camel.beans.cluster.servers[10].port = 1010
> {noformat}
> gives a list of 10 servers, {{h0:1000}} to {{h9:1009}}. The server {{h10:1010}} is gone, and there is no error or warning. With 12 servers, {{h10}} and {{h11}} are both lost. The same happens with the properties of a YAML or XML bean, and wherever a map of properties is bound with {{PropertyBindingSupport}}.
> Two things combine:
> * {{bindProperties}} sorts the keys with {{PropertyBindingKeyComparator}}: first by the number of dots, then references first, then by plain {{String.compareTo}}. So the keys of element 10 are bound before those of element 1, because the character 0 sorts before the closing bracket.
> * When a key goes through a list element, {{getOrCreatePropertyOgnlPathViaReflection}} and {{getOrCreatePropertyOgnlPathViaConfigurer}} look the element up with {{list.size() > idx ? list.get(idx) : null}}. If there is none, they create it and call {{list.add(instance)}}, which appends it at the end of the list, whatever the index is.
> With the keys above, element 0 is created at index 0. The host of element 10 is then appended at index 1 and its port, which still finds no element at index 10, at index 2. The keys of element 1 then find the element at index 1 and overwrite the host {{h10}}, and those of element 2 fill in the port-only element at index 2. Element 10 is lost.
> The same append also spreads the keys of one element over several elements whenever the index is not the next free position, that is when the elements are numbered from 1 or have a gap:
> {noformat}
> servers[1].host=a, servers[1].port=1
> -> [Server{host=a, port=0}, Server{host=null, port=1}]
> expected [null, Server{host=a, port=1}]
> servers[0].host=a, servers[5].host=z, servers[5].port=9
> -> [Server{host=a, port=0}, Server{host=z, port=0}, Server{host=null, port=9}]
> expected [Server{host=a, port=0}, null, null, null, null, Server{host=z, port=9}]
> {noformat}
> The rest of the list handling already places a value at its index and pads with null:
> * a single key such as {{names\[2\]=x}} uses {{ObjectHelper.addListByIndex}},
> * an array property is enlarged to the index,
> * a list whose elements are declared first ({{servers\[3\]=#class:...}}, the CAMEL-15396 example) is right, because the declaring key creates the element at its index before the nested keys are bound.
> Only an element created for a nested key is appended. Nothing documents that, and no test covers more than 2 nested elements.
> *The list key "last"*
> The documentation (property-binding.adoc) and the class javadoc say: "To refer to the last element, then use last as key." Every list and array index is parsed with {{Integer.parseInt}}, so {{names\[last\]=z}} and {{servers\[last\].port=7}} fail with {{NumberFormatException: For input string: "last"}}. The sentence has been in the javadoc since 3.0.0 and was never implemented. (An empty key in a nested key, {{servers\[\].port}}, does refer to the last element, but that is not documented.) The wording probably comes from the Simple language, where {{last}} works as a list index.
> h3. Reproduction
> A {{Config}} bean with a list of {{Server}} (host, port) and a list of String, bound with {{PropertyBindingSupport.build().bind(context, config, map)}} on main: the results above. The first example also fails through Camel Main itself: 12 servers bound with {{camel.beans.cluster.servers\[i\].host/port}} give 10 servers. The same results come with the options Camel Main uses for {{camel.beans}} (mandatory, ignore case, remove parameters), and with a configurer that implements {{getCollectionValueType}}. A small formal model (Lean 4) of the element lookup shows that, for any list and any index beyond its end, two keys with the same index are bound on two different elements, and that the new element is never at the index of the key. The model does not include the key order, so it does not cover the case with 11 or more elements; that one was found by a test and then traced through the comparator.
> h3. Affected versions
> The append ({{list.add(instance)}} in both methods) and the string order of the keys both came in 3.5.0, with nested list binding (CAMEL-15396), and both are in every release since, up to main. {{last}} is documented from 3.0.0 on and was never implemented.
> h3. Proposed fix
> * In both getOrCreate methods, create a missing element at its index with {{ObjectHelper.addListByIndex(list, idx, instance)}}, as the single key already does, and keep {{list.add}} only for the empty key. {{addListByIndex}} sets the element when the index is inside the list, so a null slot left by padding is filled in place and nothing shifts.
> * Resolve the list key {{last}} to the index of the last element (0 for an empty list) where a list index is parsed. Arrays keep numeric indexes. If {{last}} is not wanted, the alternative is to remove the sentence from the documentation.
> * Documentation: say that an index beyond the end of the list pads it with null.
> Sorting the digits inside the brackets as numbers would hide the case with 11 or more elements, but not the gaps. With the fix above the order of the keys no longer matters, so the comparator can stay as it is.
> h3. Compatibility
> A configuration that numbers its elements from 1, or leaves a gap, with one key per element ({{servers\[1\].host=a}}, {{servers\[2\].host=b}}) works today by accident: the elements are appended and the list has no gap ({{\[a, b\]}}). With the fix the list is {{\[null, a, b\]}}, as for a single key or an array, and code that iterates the list can hit the null. The pull request adds a note to the 4.23 upgrade guide.
> Duplicate check (2026-09-28): JIRA text "PropertyBindingSupport" (42 issues), "list index", "nested list", "list binding", "property binding" with index, and "camel.beans" with list or array: only CAMEL-15396 (gaps, right for declared elements) and CAMEL-15394 (root object with lists) are related, neither is about the index of a nested element, the key order or "last". Open pull requests: #26983 (CAMEL-25083, camel-support ObjectHelper) and #26984 (CAMEL-25084, camel-util ObjectHelper) do not change this code or {{addListByIndex}}. CAMEL-25009 (In Progress) changed the "#class:" parameters in the same file; no overlap.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)