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> { ...@@ -31,8 +31,15 @@ abstract class AggregateBinder<T> {
private final BindContext context; private final BindContext context;
AggregateBinder(BindContext context) { private final boolean allowRecursiveBinding;
AggregateBinder(BindContext context, boolean allowRecursiveBinding) {
this.context = context; this.context = context;
this.allowRecursiveBinding = allowRecursiveBinding;
}
boolean isAllowRecursiveBinding() {
return this.allowRecursiveBinding;
} }
/** /**
......
...@@ -188,13 +188,14 @@ public class Binder { ...@@ -188,13 +188,14 @@ public class Binder {
} }
protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target, protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean) { BindHandler handler, Context context, boolean allowRecursiveBinding) {
context.clearConfigurationProperty(); context.clearConfigurationProperty();
try { try {
if (!handler.onStart(name, target, context)) { if (!handler.onStart(name, target, context)) {
return null; 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); return handleBindResult(name, target, handler, context, bound);
} }
catch (Exception ex) { catch (Exception ex) {
...@@ -232,7 +233,7 @@ public class Binder { ...@@ -232,7 +233,7 @@ public class Binder {
} }
private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean) BindHandler handler, Context context, boolean allowRecursiveBinding)
throws Exception { throws Exception {
ConfigurationProperty property = findProperty(name, context); ConfigurationProperty property = findProperty(name, context);
if (property == null && containsNoDescendantOf(context.streamSources(), name)) { if (property == null && containsNoDescendantOf(context.streamSources(), name)) {
...@@ -245,7 +246,7 @@ public class Binder { ...@@ -245,7 +246,7 @@ public class Binder {
if (property != null) { if (property != null) {
return bindProperty(name, target, handler, context, property); 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) { private AggregateBinder<?> getAggregateBinder(Bindable<?> target, Context context) {
...@@ -266,7 +267,7 @@ public class Binder { ...@@ -266,7 +267,7 @@ public class Binder {
BindHandler handler, Context context, AggregateBinder<?> aggregateBinder) { BindHandler handler, Context context, AggregateBinder<?> aggregateBinder) {
AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> { AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context,
false); aggregateBinder.isAllowRecursiveBinding());
return context.withSource(source, supplier); return context.withSource(source, supplier);
}; };
return context.withIncreasedDepth( return context.withIncreasedDepth(
...@@ -290,15 +291,15 @@ public class Binder { ...@@ -290,15 +291,15 @@ public class Binder {
} }
private Object bindBean(ConfigurationPropertyName name, Bindable<?> target, private Object bindBean(ConfigurationPropertyName name, Bindable<?> target,
BindHandler handler, Context context, boolean skipIfHasBoundBean) { BindHandler handler, Context context, boolean allowRecursiveBinding) {
if (containsNoDescendantOf(context.streamSources(), name) if (containsNoDescendantOf(context.streamSources(), name)
|| isUnbindableBean(name, target, context)) { || isUnbindableBean(name, target, context)) {
return null; return null;
} }
BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind( BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(
name.append(propertyName), propertyTarget, handler, context, true); name.append(propertyName), propertyTarget, handler, context, false);
Class<?> type = target.getType().resolve(); Class<?> type = target.getType().resolve();
if (skipIfHasBoundBean && context.hasBoundBean(type)) { if (!allowRecursiveBinding && context.hasBoundBean(type)) {
return null; return null;
} }
return context.withBean(type, () -> { return context.withBean(type, () -> {
......
...@@ -45,7 +45,7 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> { ...@@ -45,7 +45,7 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> {
private static final String INDEX_ZERO = "[0]"; private static final String INDEX_ZERO = "[0]";
IndexedElementsBinder(BindContext context) { IndexedElementsBinder(BindContext context) {
super(context); super(context, false);
} }
protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target, protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target,
......
...@@ -41,7 +41,7 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> { ...@@ -41,7 +41,7 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
.mapOf(String.class, String.class); .mapOf(String.class, String.class);
MapBinder(BindContext context) { MapBinder(BindContext context) {
super(context); super(context, true);
} }
@Override @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