Merge pull request #14549 from qct:change-to-datasize

* pr/14549:
  Polish "Migrate size properties to DataSize"
  Migrate size properties to DataSize
pull/14569/merge
Stephane Nicoll 6 years ago
commit dda875033b

@ -21,29 +21,27 @@ import java.io.File;
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.util.unit.DataSize;
/**
* External configuration properties for {@link DiskSpaceHealthIndicator}.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 1.2.0
*/
@ConfigurationProperties(prefix = "management.health.diskspace")
public class DiskSpaceHealthIndicatorProperties {
private static final int MEGABYTES = 1024 * 1024;
private static final int DEFAULT_THRESHOLD = 10 * MEGABYTES;
/**
* Path used to compute the available disk space.
*/
private File path = new File(".");
/**
* Minimum disk space, in bytes, that should be available.
* Minimum disk space that should be available.
*/
private long threshold = DEFAULT_THRESHOLD;
private DataSize threshold = DataSize.ofMegabytes(10);
public File getPath() {
return this.path;
@ -55,12 +53,12 @@ public class DiskSpaceHealthIndicatorProperties {
this.path = path;
}
public long getThreshold() {
public DataSize getThreshold() {
return this.threshold;
}
public void setThreshold(long threshold) {
Assert.isTrue(threshold >= 0, "threshold must be greater than 0");
public void setThreshold(DataSize threshold) {
Assert.isTrue(!threshold.isNegative(), "threshold must be greater than 0");
this.threshold = threshold;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -18,11 +18,13 @@ package org.springframework.boot.actuate.autoconfigure.system;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.util.unit.DataSize;
import static org.assertj.core.api.Assertions.assertThat;
@ -30,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link DiskSpaceHealthIndicatorAutoConfiguration}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
public class DiskSpaceHealthIndicatorAutoConfigurationTests {
@ -45,6 +48,28 @@ public class DiskSpaceHealthIndicatorAutoConfigurationTests {
.doesNotHaveBean(ApplicationHealthIndicator.class));
}
@Test
public void thresholdMustBePositive() {
this.contextRunner
.withPropertyValues("management.health.diskspace.threshold=-10MB")
.run((context) -> assertThat(context).hasFailed().getFailure()
.hasMessageContaining(
"Failed to bind properties under 'management.health.diskspace'"));
}
@Test
public void thresholdCanBeCustomized() {
this.contextRunner
.withPropertyValues("management.health.diskspace.threshold=20MB")
.run((context) -> {
assertThat(context).hasSingleBean(DiskSpaceHealthIndicator.class);
DirectFieldAccessor dfa = new DirectFieldAccessor(
context.getBean(DiskSpaceHealthIndicator.class));
assertThat(dfa.getPropertyValue("threshold"))
.isEqualTo(DataSize.ofMegabytes(20));
});
}
@Test
public void runWhenDisabledShouldNotCreateIndicator() {
this.contextRunner.withPropertyValues("management.health.diskspace.enabled:false")

@ -25,6 +25,7 @@ import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.util.unit.DataSize;
/**
* A {@link HealthIndicator} that checks available disk space and reports a status of
@ -32,6 +33,7 @@ import org.springframework.boot.actuate.health.Status;
*
* @author Mattias Severson
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 2.0.0
*/
public class DiskSpaceHealthIndicator extends AbstractHealthIndicator {
@ -40,35 +42,47 @@ public class DiskSpaceHealthIndicator extends AbstractHealthIndicator {
private final File path;
private final long threshold;
private final DataSize threshold;
/**
* Create a new {@code DiskSpaceHealthIndicator} instance.
* @param path the Path used to compute the available disk space
* @param threshold the minimum disk space that should be available (in bytes)
* @param threshold the minimum disk space that should be available
*/
public DiskSpaceHealthIndicator(File path, long threshold) {
public DiskSpaceHealthIndicator(File path, DataSize threshold) {
super("DiskSpace health check failed");
this.path = path;
this.threshold = threshold;
}
/**
* Create a new {@code DiskSpaceHealthIndicator} instance.
* @param path the Path used to compute the available disk space
* @param threshold the minimum disk space that should be available (in bytes)
* @deprecated since 2.1.0 in favour of
* {@link #DiskSpaceHealthIndicator(File, DataSize)}
*/
@Deprecated
public DiskSpaceHealthIndicator(File path, long threshold) {
this(path, DataSize.ofBytes(threshold));
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
long diskFreeInBytes = this.path.getUsableSpace();
if (diskFreeInBytes >= this.threshold) {
if (diskFreeInBytes >= this.threshold.toBytes()) {
builder.up();
}
else {
logger.warn(String.format(
"Free disk space below threshold. "
+ "Available: %d bytes (threshold: %d bytes)",
+ "Available: %d bytes (threshold: %s)",
diskFreeInBytes, this.threshold));
builder.down();
}
builder.withDetail("total", this.path.getTotalSpace())
.withDetail("free", diskFreeInBytes)
.withDetail("threshold", this.threshold);
.withDetail("threshold", this.threshold.toBytes());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -28,6 +28,7 @@ import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.util.unit.DataSize;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@ -36,10 +37,13 @@ import static org.mockito.BDDMockito.given;
* Tests for {@link DiskSpaceHealthIndicator}.
*
* @author Mattias Severson
* @author Stephane Nicoll
*/
public class DiskSpaceHealthIndicatorTests {
static final long THRESHOLD_BYTES = 1024;
private static final DataSize THRESHOLD = DataSize.ofKilobytes(1);
private static final DataSize TOTAL_SPACE = DataSize.ofKilobytes(10);
@Rule
public ExpectedException exception = ExpectedException.none();
@ -54,30 +58,31 @@ public class DiskSpaceHealthIndicatorTests {
MockitoAnnotations.initMocks(this);
given(this.fileMock.exists()).willReturn(true);
given(this.fileMock.canRead()).willReturn(true);
this.healthIndicator = new DiskSpaceHealthIndicator(this.fileMock,
THRESHOLD_BYTES);
this.healthIndicator = new DiskSpaceHealthIndicator(this.fileMock, THRESHOLD);
}
@Test
public void diskSpaceIsUp() {
given(this.fileMock.getUsableSpace()).willReturn(THRESHOLD_BYTES + 10);
given(this.fileMock.getTotalSpace()).willReturn(THRESHOLD_BYTES * 10);
long freeSpace = THRESHOLD.toBytes() + 10;
given(this.fileMock.getUsableSpace()).willReturn(freeSpace);
given(this.fileMock.getTotalSpace()).willReturn(TOTAL_SPACE.toBytes());
Health health = this.healthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails().get("threshold")).isEqualTo(THRESHOLD_BYTES);
assertThat(health.getDetails().get("free")).isEqualTo(THRESHOLD_BYTES + 10);
assertThat(health.getDetails().get("total")).isEqualTo(THRESHOLD_BYTES * 10);
assertThat(health.getDetails().get("threshold")).isEqualTo(THRESHOLD.toBytes());
assertThat(health.getDetails().get("free")).isEqualTo(freeSpace);
assertThat(health.getDetails().get("total")).isEqualTo(TOTAL_SPACE.toBytes());
}
@Test
public void diskSpaceIsDown() {
given(this.fileMock.getUsableSpace()).willReturn(THRESHOLD_BYTES - 10);
given(this.fileMock.getTotalSpace()).willReturn(THRESHOLD_BYTES * 10);
long freeSpace = THRESHOLD.toBytes() - 10;
given(this.fileMock.getUsableSpace()).willReturn(freeSpace);
given(this.fileMock.getTotalSpace()).willReturn(TOTAL_SPACE.toBytes());
Health health = this.healthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails().get("threshold")).isEqualTo(THRESHOLD_BYTES);
assertThat(health.getDetails().get("free")).isEqualTo(THRESHOLD_BYTES - 10);
assertThat(health.getDetails().get("total")).isEqualTo(THRESHOLD_BYTES * 10);
assertThat(health.getDetails().get("threshold")).isEqualTo(THRESHOLD.toBytes());
assertThat(health.getDetails().get("free")).isEqualTo(freeSpace);
assertThat(health.getDetails().get("total")).isEqualTo(TOTAL_SPACE.toBytes());
}
}

@ -33,12 +33,14 @@ import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.core.io.Resource;
import org.springframework.kafka.listener.ContainerProperties.AckMode;
import org.springframework.kafka.security.jaas.KafkaJaasLoginModuleInitializer;
import org.springframework.util.CollectionUtils;
import org.springframework.util.unit.DataSize;
/**
* Configuration properties for Spring for Apache Kafka.
@ -247,14 +249,14 @@ public class KafkaProperties {
/**
* Maximum amount of time the server blocks before answering the fetch request if
* there isn't sufficient data to immediately satisfy the requirement given by
* "fetch.min.bytes".
* "fetch-min-size".
*/
private Duration fetchMaxWait;
/**
* Minimum amount of data, in bytes, the server should return for a fetch request.
* Minimum amount of data the server should return for a fetch request.
*/
private Integer fetchMinSize;
private DataSize fetchMinSize;
/**
* Unique string that identifies the consumer group to which this consumer
@ -339,11 +341,11 @@ public class KafkaProperties {
this.fetchMaxWait = fetchMaxWait;
}
public Integer getFetchMinSize() {
public DataSize getFetchMinSize() {
return this.fetchMinSize;
}
public void setFetchMinSize(Integer fetchMinSize) {
public void setFetchMinSize(DataSize fetchMinSize) {
this.fetchMinSize = fetchMinSize;
}
@ -406,7 +408,7 @@ public class KafkaProperties {
.to(properties.in(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG));
map.from(this::getFetchMaxWait).asInt(Duration::toMillis)
.to(properties.in(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG));
map.from(this::getFetchMinSize)
map.from(this::getFetchMinSize).asInt(DataSize::toBytes)
.to(properties.in(ConsumerConfig.FETCH_MIN_BYTES_CONFIG));
map.from(this::getGroupId).to(properties.in(ConsumerConfig.GROUP_ID_CONFIG));
map.from(this::getHeartbeatInterval).asInt(Duration::toMillis)
@ -433,10 +435,10 @@ public class KafkaProperties {
private String acks;
/**
* Default batch size in bytes. A small batch size will make batching less common
* and may reduce throughput (a batch size of zero disables batching entirely).
* Default batch size. A small batch size will make batching less common and may
* reduce throughput (a batch size of zero disables batching entirely).
*/
private Integer batchSize;
private DataSize batchSize;
/**
* Comma-delimited list of host:port pairs to use for establishing the initial
@ -445,10 +447,10 @@ public class KafkaProperties {
private List<String> bootstrapServers;
/**
* Total bytes of memory the producer can use to buffer records waiting to be sent
* to the server.
* Total memory size the producer can use to buffer records waiting to be sent to
* the server.
*/
private Long bufferMemory;
private DataSize bufferMemory;
/**
* ID to pass to the server when making requests. Used for server-side logging.
@ -497,11 +499,11 @@ public class KafkaProperties {
this.acks = acks;
}
public Integer getBatchSize() {
public DataSize getBatchSize() {
return this.batchSize;
}
public void setBatchSize(Integer batchSize) {
public void setBatchSize(DataSize batchSize) {
this.batchSize = batchSize;
}
@ -513,11 +515,11 @@ public class KafkaProperties {
this.bootstrapServers = bootstrapServers;
}
public Long getBufferMemory() {
public DataSize getBufferMemory() {
return this.bufferMemory;
}
public void setBufferMemory(Long bufferMemory) {
public void setBufferMemory(DataSize bufferMemory) {
this.bufferMemory = bufferMemory;
}
@ -577,11 +579,11 @@ public class KafkaProperties {
Properties properties = new Properties();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this::getAcks).to(properties.in(ProducerConfig.ACKS_CONFIG));
map.from(this::getBatchSize)
map.from(this::getBatchSize).asInt(DataSize::toBytes)
.to(properties.in(ProducerConfig.BATCH_SIZE_CONFIG));
map.from(this::getBootstrapServers)
.to(properties.in(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG));
map.from(this::getBufferMemory)
map.from(this::getBufferMemory).as(DataSize::toBytes)
.to(properties.in(ProducerConfig.BUFFER_MEMORY_CONFIG));
map.from(this::getClientId)
.to(properties.in(ProducerConfig.CLIENT_ID_CONFIG));
@ -674,9 +676,9 @@ public class KafkaProperties {
private List<String> bootstrapServers;
/**
* Maximum number of memory bytes to be used for buffering across all threads.
* Maximum memory size to be used for buffering across all threads.
*/
private Integer cacheMaxBytesBuffering;
private DataSize cacheMaxSizeBuffering;
/**
* ID to pass to the server when making requests. Used for server-side logging.
@ -727,12 +729,26 @@ public class KafkaProperties {
this.bootstrapServers = bootstrapServers;
}
@DeprecatedConfigurationProperty(replacement = "spring.kafka.streams.cache-max-size-buffering")
@Deprecated
public Integer getCacheMaxBytesBuffering() {
return this.cacheMaxBytesBuffering;
return (this.cacheMaxSizeBuffering != null)
? (int) this.cacheMaxSizeBuffering.toBytes() : null;
}
@Deprecated
public void setCacheMaxBytesBuffering(Integer cacheMaxBytesBuffering) {
this.cacheMaxBytesBuffering = cacheMaxBytesBuffering;
DataSize cacheMaxSizeBuffering = (cacheMaxBytesBuffering != null)
? DataSize.ofBytes(cacheMaxBytesBuffering) : null;
setCacheMaxSizeBuffering(cacheMaxSizeBuffering);
}
public DataSize getCacheMaxSizeBuffering() {
return this.cacheMaxSizeBuffering;
}
public void setCacheMaxSizeBuffering(DataSize cacheMaxSizeBuffering) {
this.cacheMaxSizeBuffering = cacheMaxSizeBuffering;
}
public String getClientId() {
@ -769,7 +785,7 @@ public class KafkaProperties {
map.from(this::getApplicationId).to(properties.in("application.id"));
map.from(this::getBootstrapServers)
.to(properties.in(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG));
map.from(this::getCacheMaxBytesBuffering)
map.from(this::getCacheMaxSizeBuffering).asInt(DataSize::toBytes)
.to(properties.in("cache.max.bytes.buffering"));
map.from(this::getClientId)
.to(properties.in(CommonClientConfigs.CLIENT_ID_CONFIG));

@ -134,7 +134,8 @@ public class EmbeddedMongoAutoConfiguration {
if (storage != null) {
String databaseDir = storage.getDatabaseDir();
String replSetName = storage.getReplSetName();
int oplogSize = (storage.getOplogSize() != null) ? storage.getOplogSize() : 0;
int oplogSize = (storage.getOplogSize() != null)
? (int) storage.getOplogSize().toMegabytes() : 0;
builder.replication(new Storage(databaseDir, replSetName, oplogSize));
}
Integer configuredPort = this.properties.getPort();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -23,6 +23,9 @@ import java.util.Set;
import de.flapdoodle.embed.mongo.distribution.Feature;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
/**
* Configuration properties for Embedded Mongo.
@ -70,9 +73,10 @@ public class EmbeddedMongoProperties {
public static class Storage {
/**
* Maximum size of the oplog, in megabytes.
* Maximum size of the oplog.
*/
private Integer oplogSize;
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize oplogSize;
/**
* Name of the replica set.
@ -84,11 +88,11 @@ public class EmbeddedMongoProperties {
*/
private String databaseDir;
public Integer getOplogSize() {
public DataSize getOplogSize() {
return this.oplogSize;
}
public void setOplogSize(Integer oplogSize) {
public void setOplogSize(DataSize oplogSize) {
this.oplogSize = oplogSize;
}

@ -57,6 +57,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.util.MimeType;
import org.springframework.util.unit.DataSize;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
/**
@ -283,8 +284,8 @@ public class ThymeleafAutoConfiguration {
PropertyMapper map = PropertyMapper.get();
map.from(properties::getMediaTypes).whenNonNull()
.to(resolver::setSupportedMediaTypes);
map.from(properties::getMaxChunkSize).when((size) -> size > 0)
.to(resolver::setResponseMaxChunkSizeBytes);
map.from(properties::getMaxChunkSize).asInt(DataSize::toBytes)
.when((size) -> size > 0).to(resolver::setResponseMaxChunkSizeBytes);
map.from(properties::getFullModeViewNames).to(resolver::setFullModeViewNames);
map.from(properties::getChunkedModeViewNames)
.to(resolver::setChunkedModeViewNames);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -23,6 +23,7 @@ import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.MediaType;
import org.springframework.util.MimeType;
import org.springframework.util.unit.DataSize;
/**
* Properties for Thymeleaf.
@ -233,10 +234,10 @@ public class ThymeleafProperties {
public static class Reactive {
/**
* Maximum size of data buffers used for writing to the response, in bytes.
* Templates will execute in CHUNKED mode by default if this is set.
* Maximum size of data buffers used for writing to the response. Templates will
* execute in CHUNKED mode by default if this is set.
*/
private int maxChunkSize;
private DataSize maxChunkSize = DataSize.ofBytes(0);
/**
* Media types supported by the view technology.
@ -263,11 +264,11 @@ public class ThymeleafProperties {
this.mediaTypes = mediaTypes;
}
public int getMaxChunkSize() {
public DataSize getMaxChunkSize() {
return this.maxChunkSize;
}
public void setMaxChunkSize(int maxChunkSize) {
public void setMaxChunkSize(DataSize maxChunkSize) {
this.maxChunkSize = maxChunkSize;
}

@ -325,14 +325,14 @@ public class ServerProperties {
private int minSpareThreads = 10;
/**
* Maximum size, in bytes, of the HTTP post content.
* Maximum size of the HTTP post content.
*/
private int maxHttpPostSize = 2097152;
private DataSize maxHttpPostSize = DataSize.ofMegabytes(2);
/**
* Maximum size, in bytes, of the HTTP message header.
*/
private int maxHttpHeaderSize = 0;
private DataSize maxHttpHeaderSize = DataSize.ofBytes(0);
/**
* Maximum amount of request body to swallow.
@ -397,11 +397,11 @@ public class ServerProperties {
this.minSpareThreads = minSpareThreads;
}
public int getMaxHttpPostSize() {
public DataSize getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
public void setMaxHttpPostSize(DataSize maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
@ -499,12 +499,12 @@ public class ServerProperties {
@Deprecated
@DeprecatedConfigurationProperty(replacement = "server.max-http-header-size")
public int getMaxHttpHeaderSize() {
public DataSize getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize;
}
@Deprecated
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
public void setMaxHttpHeaderSize(DataSize maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
@ -722,9 +722,9 @@ public class ServerProperties {
private final Accesslog accesslog = new Accesslog();
/**
* Maximum size, in bytes, of the HTTP post or put content.
* Maximum size of the HTTP post or put content.
*/
private int maxHttpPostSize = 200000; // bytes
private DataSize maxHttpPostSize = DataSize.ofBytes(200000);
/**
* Number of acceptor threads to use. When the value is -1, the default, the
@ -742,11 +742,11 @@ public class ServerProperties {
return this.accesslog;
}
public int getMaxHttpPostSize() {
public DataSize getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
public void setMaxHttpPostSize(DataSize maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
@ -937,16 +937,16 @@ public class ServerProperties {
public static class Undertow {
/**
* Maximum size, in bytes, of the HTTP post content. When the value is -1, the
* default, the size is unlimited.
* Maximum size of the HTTP post content. When the value is -1, the default, the
* size is unlimited.
*/
private long maxHttpPostSize = -1; // bytes
private DataSize maxHttpPostSize = DataSize.ofBytes(-1);
/**
* Size of each buffer, in bytes. The default is derived from the maximum amount
* of memory that is available to the JVM.
* Size of each buffer. The default is derived from the maximum amount of memory
* that is available to the JVM.
*/
private Integer bufferSize;
private DataSize bufferSize;
/**
* Number of I/O threads to create for the worker. The default is derived from the
@ -972,19 +972,19 @@ public class ServerProperties {
private final Accesslog accesslog = new Accesslog();
public long getMaxHttpPostSize() {
public DataSize getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(long maxHttpPostSize) {
public void setMaxHttpPostSize(DataSize maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Integer getBufferSize() {
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(Integer bufferSize) {
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}

@ -78,7 +78,8 @@ public class JettyWebServerFactoryCustomizer implements
.asInt(DataSize::toBytes)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize));
propertyMapper.from(jettyProperties::getMaxHttpPostSize).when(this::isPositive)
propertyMapper.from(jettyProperties::getMaxHttpPostSize).asInt(DataSize::toBytes)
.when(this::isPositive)
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
maxHttpPostSize));
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()

@ -92,7 +92,7 @@ public class TomcatWebServerFactoryCustomizer implements
propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull()
.asInt(DataSize::toBytes)
.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
propertyMapper.from(tomcatProperties::getMaxHttpPostSize)
propertyMapper.from(tomcatProperties::getMaxHttpPostSize).asInt(DataSize::toBytes)
.when((maxHttpPostSize) -> maxHttpPostSize != 0)
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
maxHttpPostSize));
@ -118,9 +118,8 @@ public class TomcatWebServerFactoryCustomizer implements
@SuppressWarnings("deprecation")
private DataSize determineMaxHttpHeaderSize() {
return isPositive(this.serverProperties.getTomcat().getMaxHttpHeaderSize())
? DataSize
.ofBytes(this.serverProperties.getTomcat().getMaxHttpHeaderSize())
return (this.serverProperties.getTomcat().getMaxHttpHeaderSize().toBytes() > 0)
? this.serverProperties.getTomcat().getMaxHttpHeaderSize()
: this.serverProperties.getMaxHttpHeaderSize();
}

@ -64,7 +64,8 @@ public class UndertowWebServerFactoryCustomizer implements
ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties
.getAccesslog();
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
propertyMapper.from(undertowProperties::getBufferSize).to(factory::setBufferSize);
propertyMapper.from(undertowProperties::getBufferSize).whenNonNull()
.asInt(DataSize::toBytes).to(factory::setBufferSize);
propertyMapper.from(undertowProperties::getIoThreads).to(factory::setIoThreads);
propertyMapper.from(undertowProperties::getWorkerThreads)
.to(factory::setWorkerThreads);
@ -88,7 +89,8 @@ public class UndertowWebServerFactoryCustomizer implements
.asInt(DataSize::toBytes)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize));
propertyMapper.from(undertowProperties::getMaxHttpPostSize).when(this::isPositive)
propertyMapper.from(undertowProperties::getMaxHttpPostSize)
.asInt(DataSize::toBytes).when(this::isPositive)
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
maxHttpPostSize));
propertyMapper.from(properties::getConnectionTimeout)

@ -29,6 +29,12 @@
"level": "error"
}
},
{
"name": "server.compression.min-response-size",
"description": "Minimum \"Content-Length\" value that is required for compression to be performed.",
"type": "org.springframework.util.unit.DataSize",
"defaultValue": "2KB"
},
{
"name": "server.error.include-stacktrace",
"defaultValue": "never"

@ -100,7 +100,7 @@ public class KafkaAutoConfigurationTests {
"spring.kafka.consumer.enable-auto-commit=false",
"spring.kafka.consumer.fetch-max-wait=456",
"spring.kafka.consumer.properties.fiz.buz=fix.fox",
"spring.kafka.consumer.fetch-min-size=789",
"spring.kafka.consumer.fetch-min-size=1KB",
"spring.kafka.consumer.group-id=bar",
"spring.kafka.consumer.heartbeat-interval=234",
"spring.kafka.consumer.key-deserializer = org.apache.kafka.common.serialization.LongDeserializer",
@ -143,7 +143,7 @@ public class KafkaAutoConfigurationTests {
assertThat(configs.get(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG))
.isEqualTo(456);
assertThat(configs.get(ConsumerConfig.FETCH_MIN_BYTES_CONFIG))
.isEqualTo(789);
.isEqualTo(1024);
assertThat(configs.get(ConsumerConfig.GROUP_ID_CONFIG))
.isEqualTo("bar");
assertThat(configs.get(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG))
@ -166,10 +166,10 @@ public class KafkaAutoConfigurationTests {
public void producerProperties() {
this.contextRunner.withPropertyValues("spring.kafka.clientId=cid",
"spring.kafka.properties.foo.bar.baz=qux.fiz.buz",
"spring.kafka.producer.acks=all", "spring.kafka.producer.batch-size=20",
"spring.kafka.producer.acks=all", "spring.kafka.producer.batch-size=2KB",
"spring.kafka.producer.bootstrap-servers=bar:1234", // test
// override
"spring.kafka.producer.buffer-memory=12345",
"spring.kafka.producer.buffer-memory=4KB",
"spring.kafka.producer.compression-type=gzip",
"spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.LongSerializer",
"spring.kafka.producer.retries=2",
@ -194,11 +194,11 @@ public class KafkaAutoConfigurationTests {
// producer
assertThat(configs.get(ProducerConfig.ACKS_CONFIG)).isEqualTo("all");
assertThat(configs.get(ProducerConfig.BATCH_SIZE_CONFIG))
.isEqualTo(20);
.isEqualTo(2048);
assertThat(configs.get(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG))
.isEqualTo(Collections.singletonList("bar:1234")); // override
assertThat(configs.get(ProducerConfig.BUFFER_MEMORY_CONFIG))
.isEqualTo(12345L);
.isEqualTo(4096L);
assertThat(configs.get(ProducerConfig.COMPRESSION_TYPE_CONFIG))
.isEqualTo("gzip");
assertThat(configs.get(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG))
@ -290,7 +290,7 @@ public class KafkaAutoConfigurationTests {
"spring.application.name=appName",
"spring.kafka.properties.foo.bar.baz=qux.fiz.buz",
"spring.kafka.streams.auto-startup=false",
"spring.kafka.streams.cache-max-bytes-buffering=42",
"spring.kafka.streams.cache-max-size-buffering=1KB",
"spring.kafka.streams.client-id=override",
"spring.kafka.streams.properties.fiz.buz=fix.fox",
"spring.kafka.streams.replication-factor=2",
@ -311,7 +311,7 @@ public class KafkaAutoConfigurationTests {
.isEqualTo("localhost:9092, localhost:9093");
assertThat(
configs.get(StreamsConfig.CACHE_MAX_BYTES_BUFFERING_CONFIG))
.isEqualTo("42");
.isEqualTo("1024");
assertThat(configs.get(StreamsConfig.CLIENT_ID_CONFIG))
.isEqualTo("override");
assertThat(configs.get(StreamsConfig.REPLICATION_FACTOR_CONFIG))
@ -347,6 +347,22 @@ public class KafkaAutoConfigurationTests {
});
}
@Test
@Deprecated
public void streamPropertiesWithCustomCacheMaxBytesBuffering() {
this.contextRunner.withUserConfiguration(EnableKafkaStreamsConfiguration.class)
.withPropertyValues("spring.application.name=appName",
"spring.kafka.streams.cache-max-bytes-buffering=42")
.run((context) -> {
Properties configs = context.getBean(
KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME,
KafkaStreamsConfiguration.class).asProperties();
assertThat(
configs.get(StreamsConfig.CACHE_MAX_BYTES_BUFFERING_CONFIG))
.isEqualTo("42");
});
}
@Test
public void streamsApplicationIdUsesMainApplicationNameByDefault() {
this.contextRunner.withUserConfiguration(EnableKafkaStreamsConfiguration.class)

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -139,6 +139,13 @@ public class EmbeddedMongoAutoConfigurationTests {
@Test
public void customOpLogSizeIsAppliedToConfiguration() {
load("spring.mongodb.embedded.storage.oplogSize=1024KB");
assertThat(this.context.getBean(IMongodConfig.class).replication().getOplogSize())
.isEqualTo(1);
}
@Test
public void customOpLogSizeUsesMegabytesPerDefault() {
load("spring.mongodb.embedded.storage.oplogSize=10");
assertThat(this.context.getBean(IMongodConfig.class).replication().getOplogSize())
.isEqualTo(10);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-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.
@ -116,7 +116,7 @@ public class ThymeleafReactiveAutoConfigurationTests {
@Test
public void overrideMaxChunkSize() {
load(BaseConfiguration.class, "spring.thymeleaf.reactive.maxChunkSize:8192");
load(BaseConfiguration.class, "spring.thymeleaf.reactive.maxChunkSize:8KB");
ThymeleafReactiveViewResolver views = this.context
.getBean(ThymeleafReactiveViewResolver.class);
assertThat(views.getResponseMaxChunkSizeBytes()).isEqualTo(Integer.valueOf(8192));

@ -226,7 +226,7 @@ public class ServerPropertiesTests {
@Test
public void tomcatMaxHttpPostSizeMatchesConnectorDefault() throws Exception {
assertThat(this.properties.getTomcat().getMaxHttpPostSize())
assertThat(this.properties.getTomcat().getMaxHttpPostSize().toBytes())
.isEqualTo(getDefaultConnector().getMaxPostSize());
}
@ -333,7 +333,7 @@ public class ServerPropertiesTests {
String message = failure.get().getCause().getMessage();
int defaultMaxPostSize = Integer
.valueOf(message.substring(message.lastIndexOf(' ')).trim());
assertThat(this.properties.getJetty().getMaxHttpPostSize())
assertThat(this.properties.getJetty().getMaxHttpPostSize().toBytes())
.isEqualTo(defaultMaxPostSize);
}
finally {
@ -343,7 +343,7 @@ public class ServerPropertiesTests {
@Test
public void undertowMaxHttpPostSizeMatchesDefault() {
assertThat(this.properties.getUndertow().getMaxHttpPostSize())
assertThat(this.properties.getUndertow().getMaxHttpPostSize().toBytes())
.isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE);
}

@ -183,7 +183,7 @@ content into your application. Rather, pick only the properties that you need.
server.compression.enabled=false # Whether response compression is enabled.
server.compression.excluded-user-agents= # List of user-agents to exclude from compression.
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml # Comma-separated list of MIME types that should be compressed.
server.compression.min-response-size=2048 # Minimum "Content-Length" value that is required for compression to be performed.
server.compression.min-response-size=2KB # Minimum "Content-Length" value that is required for compression to be performed.
server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.
server.error.include-exception=false # Include the "exception" attribute.
server.error.include-stacktrace=never # When to include a "stacktrace" attribute.
@ -203,7 +203,7 @@ content into your application. Rather, pick only the properties that you need.
server.jetty.accesslog.log-server=false # Enable logging of the request hostname.
server.jetty.accesslog.retention-period=31 # Number of days before rotated log files are deleted.
server.jetty.accesslog.time-zone=GMT # Timezone of the request log.
server.jetty.max-http-post-size=200000 # Maximum size, in bytes, of the HTTP post or put content.
server.jetty.max-http-post-size=200000B # Maximum size of the HTTP post or put content.
server.jetty.selectors=-1 # Number of selector threads to use. When the value is -1, the default, the number of selectors is derived from the operating environment.
server.max-http-header-size=8KB # Maximum size of the HTTP message header.
server.port=8080 # Server HTTP port.
@ -263,7 +263,7 @@ content into your application. Rather, pick only the properties that you need.
172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|\\
172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3} # Regular expression matching trusted IP addresses.
server.tomcat.max-connections=10000 # Maximum number of connections that the server will accept and process at any given time.
server.tomcat.max-http-post-size=2097152 # Maximum size in bytes of the HTTP post content.
server.tomcat.max-http-post-size=2MB # Maximum size of the HTTP post content.
server.tomcat.max-swallow-size=2MB # Maximum amount of request body to swallow.
server.tomcat.max-threads=200 # Maximum amount of worker threads.
server.tomcat.min-spare-threads=10 # Minimum amount of worker threads.
@ -282,11 +282,11 @@ content into your application. Rather, pick only the properties that you need.
server.undertow.accesslog.prefix=access_log. # Log file name prefix.
server.undertow.accesslog.rotate=true # Whether to enable access log rotation.
server.undertow.accesslog.suffix=log # Log file name suffix.
server.undertow.buffer-size= # Size of each buffer, in bytes.
server.undertow.buffer-size= # Size of each buffer.
server.undertow.direct-buffers= # Allocate buffers outside the Java heap. The default is derived from the maximum amount of memory that is available to the JVM.
server.undertow.eager-filter-init=true # Whether servlet filters should be initialized on startup.
server.undertow.io-threads= # Number of I/O threads to create for the worker. The default is derived from the number of available processors.
server.undertow.max-http-post-size=-1 # Maximum size in bytes of the HTTP post content. When the value is -1, the default, the size is unlimited.
server.undertow.max-http-post-size=-1B # Maximum size of the HTTP post content. When the value is -1, the default, the size is unlimited.
server.undertow.worker-threads= # Number of worker threads. The default is 8 times the number of I/O threads.
# FREEMARKER ({sc-spring-boot-autoconfigure}/freemarker/FreeMarkerProperties.{sc-ext}[FreeMarkerProperties])
@ -501,7 +501,7 @@ content into your application. Rather, pick only the properties that you need.
spring.thymeleaf.prefix=classpath:/templates/ # Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.reactive.chunked-mode-view-names= # Comma-separated list of view names (patterns allowed) that should be the only ones executed in CHUNKED mode when a max chunk size is set.
spring.thymeleaf.reactive.full-mode-view-names= # Comma-separated list of view names (patterns allowed) that should be executed in FULL mode even if a max chunk size is set.
spring.thymeleaf.reactive.max-chunk-size=0 # Maximum size of data buffers used for writing to the response, in bytes.
spring.thymeleaf.reactive.max-chunk-size=0B # Maximum size of data buffers used for writing to the response.
spring.thymeleaf.reactive.media-types= # Media types supported by the view technology.
spring.thymeleaf.servlet.content-type=text/html # Content-Type value written to HTTP responses.
spring.thymeleaf.suffix=.html # Suffix that gets appended to view names when building a URL.
@ -920,7 +920,7 @@ content into your application. Rather, pick only the properties that you need.
# EMBEDDED MONGODB ({sc-spring-boot-autoconfigure}/mongo/embedded/EmbeddedMongoProperties.{sc-ext}[EmbeddedMongoProperties])
spring.mongodb.embedded.features=sync_delay # Comma-separated list of features to enable.
spring.mongodb.embedded.storage.database-dir= # Directory used for data storage.
spring.mongodb.embedded.storage.oplog-size= # Maximum size of the oplog, in megabytes.
spring.mongodb.embedded.storage.oplog-size= # Maximum size of the oplog.
spring.mongodb.embedded.storage.repl-set-name= # Name of the replica set.
spring.mongodb.embedded.version=3.2.2 # Version of Mongo to use.
@ -1046,8 +1046,8 @@ content into your application. Rather, pick only the properties that you need.
spring.kafka.consumer.bootstrap-servers= # Comma-delimited list of host:port pairs to use for establishing the initial connections to the Kafka cluster. Overrides the global property, for consumers.
spring.kafka.consumer.client-id= # ID to pass to the server when making requests. Used for server-side logging.
spring.kafka.consumer.enable-auto-commit= # Whether the consumer's offset is periodically committed in the background.
spring.kafka.consumer.fetch-max-wait= # Maximum amount of time the server blocks before answering the fetch request if there isn't sufficient data to immediately satisfy the requirement given by "fetch.min.bytes".
spring.kafka.consumer.fetch-min-size= # Minimum amount of data, in bytes, the server should return for a fetch request.
spring.kafka.consumer.fetch-max-wait= # Maximum amount of time the server blocks before answering the fetch request if there isn't sufficient data to immediately satisfy the requirement given by "fetch-min-size".
spring.kafka.consumer.fetch-min-size= # Minimum amount of data the server should return for a fetch request.
spring.kafka.consumer.group-id= # Unique string that identifies the consumer group to which this consumer belongs.
spring.kafka.consumer.heartbeat-interval= # Expected time between heartbeats to the consumer coordinator.
spring.kafka.consumer.key-deserializer= # Deserializer class for keys.
@ -1078,9 +1078,9 @@ content into your application. Rather, pick only the properties that you need.
spring.kafka.listener.poll-timeout= # Timeout to use when polling the consumer.
spring.kafka.listener.type=single # Listener type.
spring.kafka.producer.acks= # Number of acknowledgments the producer requires the leader to have received before considering a request complete.
spring.kafka.producer.batch-size= # Default batch size in bytes.
spring.kafka.producer.batch-size= # Default batch size.
spring.kafka.producer.bootstrap-servers= # Comma-delimited list of host:port pairs to use for establishing the initial connections to the Kafka cluster. Overrides the global property, for producers.
spring.kafka.producer.buffer-memory= # Total bytes of memory the producer can use to buffer records waiting to be sent to the server.
spring.kafka.producer.buffer-memory= # Total memory size the producer can use to buffer records waiting to be sent to the server.
spring.kafka.producer.client-id= # ID to pass to the server when making requests. Used for server-side logging.
spring.kafka.producer.compression-type= # Compression type for all data generated by the producer.
spring.kafka.producer.key-serializer= # Serializer class for keys.
@ -1108,7 +1108,7 @@ content into your application. Rather, pick only the properties that you need.
spring.kafka.streams.application-id= # Kafka streams application.id property; default spring.application.name.
spring.kafka.streams.auto-startup=true # Whether or not to auto-start the streams factory bean.
spring.kafka.streams.bootstrap-servers= # Comma-delimited list of host:port pairs to use for establishing the initial connections to the Kafka cluster. Overrides the global property, for streams.
spring.kafka.streams.cache-max-bytes-buffering= # Maximum number of memory bytes to be used for buffering across all threads.
spring.kafka.streams.cache-max-size-buffering= # Maximum number of memory size to be used for buffering across all threads.
spring.kafka.streams.client-id= # ID to pass to the server when making requests. Used for server-side logging.
spring.kafka.streams.properties.*= # Additional Kafka properties used to configure the streams.
spring.kafka.streams.replication-factor= # The replication factor for change log topics and repartition topics created by the stream processing application.
@ -1347,7 +1347,7 @@ content into your application. Rather, pick only the properties that you need.
management.health.defaults.enabled=true # Whether to enable default health indicators.
management.health.diskspace.enabled=true # Whether to enable disk space health check.
management.health.diskspace.path= # Path used to compute the available disk space.
management.health.diskspace.threshold=0 # Minimum disk space, in bytes, that should be available.
management.health.diskspace.threshold=10MB # Minimum disk space that should be available.
management.health.elasticsearch.enabled=true # Whether to enable Elasticsearch health check.
management.health.elasticsearch.indices= # Comma-separated index names.
management.health.elasticsearch.response-timeout=100ms # Time to wait for a response from the cluster.

@ -41,7 +41,7 @@ final class JettyHandlerWrappers {
static HandlerWrapper createGzipHandlerWrapper(Compression compression) {
GzipHandler handler = new GzipHandler();
handler.setMinGzipSize(compression.getMinResponseSize());
handler.setMinGzipSize((int) compression.getMinResponseSize().toBytes());
handler.setIncludedMimeTypes(compression.getMimeTypes());
for (HttpMethod httpMethod : HttpMethod.values()) {
handler.addIncludedMethods(httpMethod.name());

@ -51,8 +51,9 @@ final class CompressionCustomizer implements NettyServerCustomizer {
@Override
public HttpServer apply(HttpServer server) {
if (this.compression.getMinResponseSize() >= 0) {
server = server.compress(this.compression.getMinResponseSize());
if (!this.compression.getMinResponseSize().isNegative()) {
server = server
.compress((int) this.compression.getMinResponseSize().toBytes());
}
CompressionPredicate mimeTypes = getMimeTypesPredicate(
this.compression.getMimeTypes());

@ -50,7 +50,7 @@ class CompressionConnectorCustomizer implements TomcatConnectorCustomizer {
private void customize(AbstractHttp11Protocol<?> protocol) {
Compression compression = this.compression;
protocol.setCompression("on");
protocol.setCompressionMinSize(compression.getMinResponseSize());
protocol.setCompressionMinSize((int) compression.getMinResponseSize().toBytes());
protocol.setCompressibleMimeType(
StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()));
if (this.compression.getExcludedUserAgents() != null) {

@ -65,7 +65,8 @@ final class UndertowCompressionConfigurer {
private static Predicate[] getCompressionPredicates(Compression compression) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(new MaxSizePredicate(compression.getMinResponseSize()));
predicates.add(
new MaxSizePredicate((int) compression.getMinResponseSize().toBytes()));
predicates.add(new CompressibleMimeTypePredicate(compression.getMimeTypes()));
if (compression.getExcludedUserAgents() != null) {
for (String agent : compression.getExcludedUserAgents()) {

@ -16,6 +16,8 @@
package org.springframework.boot.web.server;
import org.springframework.util.unit.DataSize;
/**
* Simple server-independent abstraction for compression configuration.
*
@ -45,7 +47,7 @@ public class Compression {
/**
* Minimum "Content-Length" value that is required for compression to be performed.
*/
private int minResponseSize = 2048;
private DataSize minResponseSize = DataSize.ofKilobytes(2);
public boolean getEnabled() {
return this.enabled;
@ -63,11 +65,11 @@ public class Compression {
this.mimeTypes = mimeTypes;
}
public int getMinResponseSize() {
public DataSize getMinResponseSize() {
return this.minResponseSize;
}
public void setMinResponseSize(int minSize) {
public void setMinResponseSize(DataSize minSize) {
this.minResponseSize = minSize;
}

@ -58,6 +58,7 @@ import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.SocketUtils;
import org.springframework.util.unit.DataSize;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
@ -265,7 +266,7 @@ public abstract class AbstractReactiveWebServerFactoryTests {
public void noCompressionForSmallResponse() {
Compression compression = new Compression();
compression.setEnabled(true);
compression.setMinResponseSize(3001);
compression.setMinResponseSize(DataSize.ofBytes(3001));
WebClient client = prepareCompressionTest(compression);
ResponseEntity<Void> response = client.get().exchange()
.flatMap((res) -> res.toEntity(Void.class)).block();

Loading…
Cancel
Save