Commit 588dcf76 authored by Phillip Webb's avatar Phillip Webb

Only allow binder recursion with Maps

Refine the binder recursion rules introduced in commit f3373238 so
they only apply to Maps. Recursive List and Array binding is now no
longer allowed. Prior to this commit, binding a List that contained a
reference to itself would fail with a `StackOverflowException` if the
underlying property source was not iterable.

Fixes gh-10702
parent 99ad79db
......@@ -31,8 +31,15 @@ abstract class AggregateBinder<T> {
private final BindContext context;
AggregateBinder(BindContext context) {
private final boolean allowRecursiveBinding;
AggregateBinder(BindContext context, boolean allowRecursiveBinding) {
this.context = context;
this.allowRecursiveBinding = allowRecursiveBinding;
}
boolean isAllowRecursiveBinding() {
return this.allowRecursiveBinding;
}
/**
......
......@@ -188,13 +188,14 @@ public class Binder {
}
protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean) {
BindHandler handler, Context context, boolean allowRecursiveBinding) {
context.clearConfigurationProperty();
try {
if (!handler.onStart(name, target, context)) {
return null;
}
Object bound = bindObject(name, target, handler, context, skipIfHasBoundBean);
Object bound = bindObject(name, target, handler, context,
allowRecursiveBinding);
return handleBindResult(name, target, handler, context, bound);
}
catch (Exception ex) {
......@@ -232,7 +233,7 @@ public class Binder {
}
private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean)
BindHandler handler, Context context, boolean allowRecursiveBinding)
throws Exception {
ConfigurationProperty property = findProperty(name, context);
if (property == null && containsNoDescendantOf(context.streamSources(), name)) {
......@@ -245,7 +246,7 @@ public class Binder {
if (property != null) {
return bindProperty(name, target, handler, context, property);
}
return bindBean(name, target, handler, context, skipIfHasBoundBean);
return bindBean(name, target, handler, context, allowRecursiveBinding);
}
private AggregateBinder<?> getAggregateBinder(Bindable<?> target, Context context) {
......@@ -266,7 +267,7 @@ public class Binder {
BindHandler handler, Context context, AggregateBinder<?> aggregateBinder) {
AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context,
false);
aggregateBinder.isAllowRecursiveBinding());
return context.withSource(source, supplier);
};
return context.withIncreasedDepth(
......@@ -290,15 +291,15 @@ public class Binder {
}
private Object bindBean(ConfigurationPropertyName name, Bindable<?> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean) {
BindHandler handler, Context context, boolean allowRecursiveBinding) {
if (containsNoDescendantOf(context.streamSources(), name)
|| isUnbindableBean(name, target, context)) {
return null;
}
BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(
name.append(propertyName), propertyTarget, handler, context, true);
name.append(propertyName), propertyTarget, handler, context, false);
Class<?> type = target.getType().resolve();
if (skipIfHasBoundBean && context.hasBoundBean(type)) {
if (!allowRecursiveBinding && context.hasBoundBean(type)) {
return null;
}
return context.withBean(type, () -> {
......
......@@ -45,7 +45,7 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> {
private static final String INDEX_ZERO = "[0]";
IndexedElementsBinder(BindContext context) {
super(context);
super(context, false);
}
protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target,
......
......@@ -41,7 +41,7 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
.mapOf(String.class, String.class);
MapBinder(BindContext context) {
super(context);
super(context, true);
}
@Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment