GH-1573 Fixed BindingHandlerAdvise for validation
Fixed BindingHandlerAdvise to ensure it creates and ValidationBindHandler instead of simple BindHandler to ensure that during the merge of properties validation is not lost. Resolves #1573
This commit is contained in:
@@ -168,7 +168,7 @@ public class ConsumerProperties {
|
||||
this.partitioned = partitioned;
|
||||
}
|
||||
|
||||
@Min(value = 1, message = "Instance count should be greater than zero.")
|
||||
@Min(value = -1, message = "Instance count should be greater than or equal to -1.")
|
||||
public int getInstanceCount() {
|
||||
return instanceCount;
|
||||
}
|
||||
@@ -177,7 +177,7 @@ public class ConsumerProperties {
|
||||
this.instanceCount = instanceCount;
|
||||
}
|
||||
|
||||
@Min(value = 0, message = "Instance index should be greater than or equal to 0")
|
||||
@Min(value = -1, message = "Instance index should be greater than or equal to -1")
|
||||
public int getInstanceIndex() {
|
||||
return instanceIndex;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,12 @@ import org.springframework.boot.context.properties.bind.BindContext;
|
||||
import org.springframework.boot.context.properties.bind.BindHandler;
|
||||
import org.springframework.boot.context.properties.bind.BindResult;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.validation.ValidationBindHandler;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -38,19 +41,22 @@ import org.springframework.util.CollectionUtils;
|
||||
public class BindingHandlerAdvise implements ConfigurationPropertiesBindHandlerAdvisor{
|
||||
|
||||
private final Map<ConfigurationPropertyName, ConfigurationPropertyName> mappings;
|
||||
|
||||
private final Validator[] validator;
|
||||
|
||||
BindingHandlerAdvise(Map<ConfigurationPropertyName, ConfigurationPropertyName> additionalMappings) {
|
||||
BindingHandlerAdvise(Map<ConfigurationPropertyName, ConfigurationPropertyName> additionalMappings, @Nullable Validator validator) {
|
||||
this.mappings = new LinkedHashMap<>();
|
||||
this.mappings.put(ConfigurationPropertyName.of("spring.cloud.stream.bindings"),
|
||||
ConfigurationPropertyName.of("spring.cloud.stream.default"));
|
||||
if (!CollectionUtils.isEmpty(additionalMappings)) {
|
||||
this.mappings.putAll(additionalMappings);
|
||||
}
|
||||
this.validator = validator != null ? new Validator[] {validator} : new Validator[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindHandler apply(BindHandler bindHandler) {
|
||||
BindHandler handler = new BindHandler() {
|
||||
BindHandler handler = new ValidationBindHandler(validator) {
|
||||
@Override
|
||||
public <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> target, BindContext context) {
|
||||
ConfigurationPropertyName defaultName = getDefaultName(name);
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.springframework.messaging.core.DestinationResolver;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
|
||||
/**
|
||||
@@ -92,7 +93,7 @@ public class BindingServiceConfiguration {
|
||||
private Collection<DefaultBinderFactory.Listener> binderFactoryListeners;
|
||||
|
||||
@Bean
|
||||
public BindingHandlerAdvise BindingHandlerAdvise(@Nullable MappingsProvider[] providers) {
|
||||
public BindingHandlerAdvise BindingHandlerAdvise(@Nullable MappingsProvider[] providers, @Nullable Validator validator) {
|
||||
Map<ConfigurationPropertyName, ConfigurationPropertyName> additionalMappings = new HashMap<>();
|
||||
if (!ObjectUtils.isEmpty(providers)) {
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
@@ -100,7 +101,7 @@ public class BindingServiceConfiguration {
|
||||
additionalMappings.putAll(mappingsProvider.getDefaultMappings());
|
||||
}
|
||||
}
|
||||
return new BindingHandlerAdvise(additionalMappings);
|
||||
return new BindingHandlerAdvise(additionalMappings, validator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.cloud.stream.config;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
// see https://github.com/spring-cloud/spring-cloud-stream/issues/1573 for more details
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public class BindingHandlerAdviseTests {
|
||||
|
||||
@Test(expected=BeanCreationException.class)
|
||||
public void testFailureWithWrongValue() {
|
||||
new SpringApplicationBuilder(SampleConfiguration.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--props.value=-1", "--spring.jmx.enabled=false");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidatedValueValue() {
|
||||
ValidatedProps validatedProps = new SpringApplicationBuilder(SampleConfiguration.class)
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--props.value=2", "--spring.jmx.enabled=false").getBean(ValidatedProps.class);
|
||||
assertEquals(2, validatedProps.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@EnableBinding(Sink.class)
|
||||
@Import(TestChannelBinderConfiguration.class)
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigurationProperties(ValidatedProps.class)
|
||||
class SampleConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties("props")
|
||||
@Validated
|
||||
class ValidatedProps {
|
||||
|
||||
@Min(0)
|
||||
private int value;
|
||||
|
||||
public void setValue(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user