Version information
Spring-boot version: 2.2.6.RELEASE
Issue reproduction and actual result
I have some simple constructor-bound ConfigurationProperties
:
package com.example.demo;
import java.nio.file.Path;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConfigurationProperties
@ConstructorBinding
public class ConfigurationPropertiesWithPathProperty {
private final Path somePathProperty;
public ConfigurationPropertiesWithPathProperty(Path somePathProperty) {
this.somePathProperty = somePathProperty;
}
public Path getSomePathProperty() {
return somePathProperty;
}
}
This test, in which I try to bind an empty string, fails:
package com.example.demo;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
public class PropertiesTest {
@Test
void bindsEmptyToPathProperty() {
Map<String, String> properties = new HashMap<>();
properties.put("some_path_property", "");
Binder binder = new Binder(new MapConfigurationPropertySource(properties));
BindResult<ConfigurationPropertiesWithPathProperty> bindResult =
binder.bind("", Bindable.of(ConfigurationPropertiesWithPathProperty.class));
assertThat(bindResult.isBound()).isTrue();
assertThat(bindResult.get().getSomePathProperty()).isEqualTo(Paths.get(""));
}
}
Additional observations
When I add a @DefaultValue
to the constructor property:
public ConfigurationPropertiesWithPathProperty(@DefaultValue("some_default") Path somePathProperty) {
this.somePathProperty = somePathProperty;
}
The same test fails with an exception. The exception is the same as in spring-projects/spring-boot#21264, but it means that binding to the default value is attempted.
Note that the same issue occurs not only with constructor-bound properties, but java bean binding as well.
Expected result
An empty string can be bound to a property of java.nio.file.Path
type resulting in a relative empty path (equal to the result of Paths.get("")
).
Comment From: philwebb
Thanks for the detailed description. The cause of the problem is that the conversion service does not support the array type used in @DefaultValue
when using the TypeConverterConverter
service.
The Path
object uses a PropertyEditor
based converter. If it has a full Converter
then the ArrayToObjectConverter
would take care of conversion.
Comment From: philwebb
From @mbhave in the original issue:
For this, we rely on Spring Framework's PathEditor
for converting from String
to Path
. The PathEditor
returns a null value if the source value is ""
. This might be something that needs to be fixed in Spring Framework. If the rest of the team agrees, this issue will need to be transferred to Spring Framework's issue tracker.
Comment From: snicoll
Unfortunately, that's how the editors work. They treat an empty value as the absence of a value. I understand that the empty string has a special meaning for Path but this could come as a surprise for users that rely on the current behavior.