diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index bee0dedb86..8ce38af30a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -112,11 +112,50 @@ public final class EndpointRequest { return new LinksRequestMatcher(); } + /** + * Base class for supported request matchers. + */ + private static abstract class AbstractRequestMatcher + extends ApplicationContextRequestMatcher { + + private volatile RequestMatcher delegate; + + public AbstractRequestMatcher() { + super(WebApplicationContext.class); + } + + @Override + protected final void initialized(Supplier context) { + this.delegate = createDelegate(context.get()); + } + + @Override + protected final boolean matches(HttpServletRequest request, + Supplier context) { + return this.delegate.matches(request); + } + + private RequestMatcher createDelegate(WebApplicationContext context) { + try { + RequestMatcherFactory requestMatcherFactory = new RequestMatcherFactory( + context.getBean(DispatcherServletPathProvider.class) + .getServletPath()); + return createDelegate(context, requestMatcherFactory); + } + catch (NoSuchBeanDefinitionException ex) { + return EMPTY_MATCHER; + } + } + + protected abstract RequestMatcher createDelegate(WebApplicationContext context, + RequestMatcherFactory requestMatcherFactory); + + } + /** * The request matcher used to match against {@link Endpoint actuator endpoints}. */ - public static final class EndpointRequestMatcher - extends ApplicationContextRequestMatcher { + public static final class EndpointRequestMatcher extends AbstractRequestMatcher { private final List includes; @@ -124,8 +163,6 @@ public final class EndpointRequest { private final boolean includeLinks; - private volatile RequestMatcher delegate; - private EndpointRequestMatcher(boolean includeLinks) { this(Collections.emptyList(), Collections.emptyList(), includeLinks); } @@ -142,7 +179,6 @@ public final class EndpointRequest { private EndpointRequestMatcher(List includes, List excludes, boolean includeLinks) { - super(WebApplicationContext.class); this.includes = includes; this.excludes = excludes; this.includeLinks = includeLinks; @@ -165,40 +201,22 @@ public final class EndpointRequest { } @Override - protected void initialized( - Supplier webApplicationContext) { - this.delegate = createDelegate(webApplicationContext); - } - - private RequestMatcher createDelegate( - Supplier webApplicationContext) { - try { - WebApplicationContext context = webApplicationContext.get(); - PathMappedEndpoints pathMappedEndpoints = context - .getBean(PathMappedEndpoints.class); - DispatcherServletPathProvider pathProvider = context - .getBean(DispatcherServletPathProvider.class); - return createDelegate(pathMappedEndpoints, pathProvider.getServletPath()); - } - catch (NoSuchBeanDefinitionException ex) { - return EMPTY_MATCHER; - } - } - - private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints, - String servletPath) { + protected RequestMatcher createDelegate(WebApplicationContext context, + RequestMatcherFactory requestMatcherFactory) { + PathMappedEndpoints pathMappedEndpoints = context + .getBean(PathMappedEndpoints.class); Set paths = new LinkedHashSet<>(); if (this.includes.isEmpty()) { paths.addAll(pathMappedEndpoints.getAllPaths()); } streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); - List delegateMatchers = getDelegateMatchers(servletPath, - paths); + List delegateMatchers = getDelegateMatchers( + requestMatcherFactory, paths); if (this.includeLinks && StringUtils.hasText(pathMappedEndpoints.getBasePath())) { - delegateMatchers.add(new AntPathRequestMatcher( - computePath(servletPath, pathMappedEndpoints.getBasePath()))); + delegateMatchers.add( + requestMatcherFactory.antPath(pathMappedEndpoints.getBasePath())); } return new OrRequestMatcher(delegateMatchers); } @@ -226,75 +244,50 @@ public final class EndpointRequest { return annotation.id(); } - private List getDelegateMatchers(String servletPath, - Set paths) { + private List getDelegateMatchers( + RequestMatcherFactory requestMatcherFactory, Set paths) { return paths.stream() - .map((path) -> new AntPathRequestMatcher(computePath(servletPath, path) + "/**")) + .map((path) -> requestMatcherFactory.antPath(path, "/**")) .collect(Collectors.toList()); } - private String computePath(String servletPath, String path) { - if (servletPath.equals("/")) { - return path; - } - return servletPath + path; - } - - @Override - protected boolean matches(HttpServletRequest request, - Supplier context) { - return this.delegate.matches(request); - } - } /** * The request matcher used to match against the links endpoint. */ - public static final class LinksRequestMatcher - extends ApplicationContextRequestMatcher { - - private RequestMatcher delegate; - - private LinksRequestMatcher() { - super(WebApplicationContext.class); - } + public static final class LinksRequestMatcher extends AbstractRequestMatcher { @Override - protected void initialized( - Supplier webApplicationContext) { - try { - WebApplicationContext context = webApplicationContext.get(); - WebEndpointProperties properties = context - .getBean(WebEndpointProperties.class); - DispatcherServletPathProvider pathProvider = context - .getBean(DispatcherServletPathProvider.class); - this.delegate = createDelegate(pathProvider.getServletPath(), properties); - } - catch (NoSuchBeanDefinitionException ex) { - this.delegate = EMPTY_MATCHER; - } - } - - private RequestMatcher createDelegate(String path, - WebEndpointProperties properties) { + protected RequestMatcher createDelegate(WebApplicationContext context, + RequestMatcherFactory requestMatcherFactory) { + WebEndpointProperties properties = context + .getBean(WebEndpointProperties.class); if (StringUtils.hasText(properties.getBasePath())) { - return new AntPathRequestMatcher(computePath(path, properties.getBasePath())); + return requestMatcherFactory.antPath(properties.getBasePath()); } return EMPTY_MATCHER; } - private String computePath(String servletPath, String path) { - if (servletPath.equals("/")) { - return path; - } - return servletPath + path; + } + + /** + * Factory used to create a {@link RequestMatcher}. + */ + private static class RequestMatcherFactory { + + private final String servletPath; + + RequestMatcherFactory(String servletPath) { + this.servletPath = servletPath; } - @Override - protected boolean matches(HttpServletRequest request, - Supplier context) { - return this.delegate.matches(request); + public RequestMatcher antPath(String... parts) { + String pattern = (this.servletPath.equals("/") ? "" : this.servletPath); + for (String part : parts) { + pattern += part; + } + return new AntPathRequestMatcher(pattern); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java index f6f5094570..8a3a79e668 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java @@ -99,8 +99,9 @@ public class EndpointMBean implements DynamicMBean { String[] parameterNames = operation.getParameters().stream() .map(JmxOperationParameter::getName).toArray(String[]::new); Map arguments = getArguments(parameterNames, params); - Object result = operation - .invoke(new InvocationContext(SecurityContext.NONE, arguments)); + InvocationContext context = new InvocationContext(SecurityContext.NONE, + arguments); + Object result = operation.invoke(context); if (REACTOR_PRESENT) { result = ReactiveHandler.handle(result); } @@ -119,9 +120,7 @@ public class EndpointMBean implements DynamicMBean { if (exception.getClass().getName().startsWith("java.")) { return exception; } - else { - return new IllegalStateException(exception.getMessage()); - } + return new IllegalStateException(exception.getMessage()); } private Map getArguments(String[] parameterNames, Object[] params) { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java index 481f9b85a9..f851e3febc 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanTests.java @@ -130,10 +130,12 @@ public class EndpointMBeanTests { public void invokeWhenOperationIsInvalidShouldThrowException() throws MBeanException, ReflectionException { TestJmxOperation operation = new TestJmxOperation() { + @Override public Object invoke(InvocationContext context) { throw new InvalidEndpointRequestException("test failure", "test"); } + }; TestExposableJmxEndpoint endpoint = new TestExposableJmxEndpoint(operation); EndpointMBean bean = new EndpointMBean(this.responseMapper, endpoint); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java index cda3209975..717bbd313b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java @@ -33,6 +33,7 @@ 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.PropertyMapper; import org.springframework.boot.convert.DurationUnit; import org.springframework.core.io.Resource; import org.springframework.kafka.listener.ContainerProperties.AckMode; @@ -192,16 +193,6 @@ public class KafkaProperties { return properties; } - private static String resourceToPath(Resource resource) { - try { - return resource.getFile().getAbsolutePath(); - } - catch (IOException ex) { - throw new IllegalStateException( - "Resource '" + resource + "' must be on a file system", ex); - } - } - public static class Consumer { private final Ssl ssl = new Ssl(); @@ -382,55 +373,32 @@ public class KafkaProperties { } public Map buildProperties() { - Map properties = new HashMap<>(); - if (this.autoCommitInterval != null) { - properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, - (int) this.autoCommitInterval.toMillis()); - } - if (this.autoOffsetReset != null) { - properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, - this.autoOffsetReset); - } - if (this.bootstrapServers != null) { - properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, - this.bootstrapServers); - } - if (this.clientId != null) { - properties.put(ConsumerConfig.CLIENT_ID_CONFIG, this.clientId); - } - if (this.enableAutoCommit != null) { - properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, - this.enableAutoCommit); - } - if (this.fetchMaxWait != null) { - properties.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, - (int) this.fetchMaxWait.toMillis()); - } - if (this.fetchMinSize != null) { - properties.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, this.fetchMinSize); - } - if (this.groupId != null) { - properties.put(ConsumerConfig.GROUP_ID_CONFIG, this.groupId); - } - if (this.heartbeatInterval != null) { - properties.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, - (int) this.heartbeatInterval.toMillis()); - } - if (this.keyDeserializer != null) { - properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, - this.keyDeserializer); - } - if (this.valueDeserializer != null) { - properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, - this.valueDeserializer); - } - if (this.maxPollRecords != null) { - properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, - this.maxPollRecords); - } - properties.putAll(this.ssl.buildProperties()); - properties.putAll(this.properties); - return properties; + Properties properties = new Properties(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(this::getAutoCommitInterval).asInt(Duration::toMillis) + .to(properties.in(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG)); + map.from(this::getAutoOffsetReset) + .to(properties.in(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG)); + map.from(this::getBootstrapServers) + .to(properties.in(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)); + map.from(this::getClientId) + .to(properties.in(ConsumerConfig.CLIENT_ID_CONFIG)); + map.from(this::getEnableAutoCommit) + .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) + .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) + .to(properties.in(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG)); + map.from(this::getKeyDeserializer) + .to(properties.in(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG)); + map.from(this::getValueDeserializer) + .to(properties.in(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG)); + map.from(this::getMaxPollRecords) + .to(properties.in(ConsumerConfig.MAX_POLL_RECORDS_CONFIG)); + return properties.with(this.ssl, this.properties); } } @@ -586,41 +554,25 @@ public class KafkaProperties { } public Map buildProperties() { - Map properties = new HashMap<>(); - if (this.acks != null) { - properties.put(ProducerConfig.ACKS_CONFIG, this.acks); - } - if (this.batchSize != null) { - properties.put(ProducerConfig.BATCH_SIZE_CONFIG, this.batchSize); - } - if (this.bootstrapServers != null) { - properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, - this.bootstrapServers); - } - if (this.bufferMemory != null) { - properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, this.bufferMemory); - } - if (this.clientId != null) { - properties.put(ProducerConfig.CLIENT_ID_CONFIG, this.clientId); - } - if (this.compressionType != null) { - properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, - this.compressionType); - } - if (this.keySerializer != null) { - properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, - this.keySerializer); - } - if (this.retries != null) { - properties.put(ProducerConfig.RETRIES_CONFIG, this.retries); - } - if (this.valueSerializer != null) { - properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, - this.valueSerializer); - } - properties.putAll(this.ssl.buildProperties()); - properties.putAll(this.properties); - return properties; + Properties properties = new Properties(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(this::getAcks).to(properties.in(ProducerConfig.ACKS_CONFIG)); + map.from(this::getBatchSize) + .to(properties.in(ProducerConfig.BATCH_SIZE_CONFIG)); + map.from(this::getBootstrapServers) + .to(properties.in(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG)); + map.from(this::getBufferMemory) + .to(properties.in(ProducerConfig.BUFFER_MEMORY_CONFIG)); + map.from(this::getClientId) + .to(properties.in(ProducerConfig.CLIENT_ID_CONFIG)); + map.from(this::getCompressionType) + .to(properties.in(ProducerConfig.COMPRESSION_TYPE_CONFIG)); + map.from(this::getKeySerializer) + .to(properties.in(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG)); + map.from(this::getRetries).to(properties.in(ProducerConfig.RETRIES_CONFIG)); + map.from(this::getValueSerializer) + .to(properties.in(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG)); + return properties.with(this.ssl, this.properties); } } @@ -669,13 +621,11 @@ public class KafkaProperties { } public Map buildProperties() { - Map properties = new HashMap<>(); - if (this.clientId != null) { - properties.put(ProducerConfig.CLIENT_ID_CONFIG, this.clientId); - } - properties.putAll(this.ssl.buildProperties()); - properties.putAll(this.properties); - return properties; + Properties properties = new Properties(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(this::getClientId) + .to(properties.in(ProducerConfig.CLIENT_ID_CONFIG)); + return properties.with(this.ssl, this.properties); } } @@ -969,40 +919,35 @@ public class KafkaProperties { } public Map buildProperties() { - Map properties = new HashMap<>(); - if (this.getKeyPassword() != null) { - properties.put(SslConfigs.SSL_KEY_PASSWORD_CONFIG, this.getKeyPassword()); - } - if (this.getKeystoreLocation() != null) { - properties.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, - resourceToPath(this.getKeystoreLocation())); - } - if (this.getKeystorePassword() != null) { - properties.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, - this.getKeystorePassword()); - } - if (this.getKeyStoreType() != null) { - properties.put(SslConfigs.SSL_KEYSTORE_TYPE_CONFIG, - this.getKeyStoreType()); - } - if (this.getTruststoreLocation() != null) { - properties.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, - resourceToPath(this.getTruststoreLocation())); - } - if (this.getTruststorePassword() != null) { - properties.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, - this.getTruststorePassword()); - } - if (this.getTrustStoreType() != null) { - properties.put(SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG, - this.getTrustStoreType()); - } - if (this.getProtocol() != null) { - properties.put(SslConfigs.SSL_PROTOCOL_CONFIG, this.getProtocol()); - } + Properties properties = new Properties(); + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(this::getKeyPassword) + .to(properties.in(SslConfigs.SSL_KEY_PASSWORD_CONFIG)); + map.from(this::getKeystoreLocation).as(this::resourceToPath) + .to(properties.in(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG)); + map.from(this::getKeystorePassword) + .to(properties.in(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG)); + map.from(this::getKeyStoreType) + .to(properties.in(SslConfigs.SSL_KEYSTORE_TYPE_CONFIG)); + map.from(this::getTruststoreLocation).as(this::resourceToPath) + .to(properties.in(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG)); + map.from(this::getTruststorePassword) + .to(properties.in(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG)); + map.from(this::getTrustStoreType) + .to(properties.in(SslConfigs.SSL_TRUSTSTORE_TYPE_CONFIG)); + map.from(this::getProtocol).to(properties.in(SslConfigs.SSL_PROTOCOL_CONFIG)); return properties; } + private String resourceToPath(Resource resource) { + try { + return resource.getFile().getAbsolutePath(); + } + catch (IOException ex) { + throw new IllegalStateException( + "Resource '" + resource + "' must be on a file system", ex); + } + } } public static class Jaas { @@ -1064,4 +1009,18 @@ public class KafkaProperties { } + private static class Properties extends HashMap { + + public java.util.function.Consumer in(String key) { + return (value) -> put(key, value); + } + + public Properties with(Ssl ssl, Map properties) { + putAll(ssl.buildProperties()); + putAll(properties); + return this; + } + + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java index c69b711a13..d1e30f4fb0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java @@ -831,7 +831,6 @@ public class WebMvcAutoConfigurationTests { ContentNegotiationStrategy delegate = mock(ContentNegotiationStrategy.class); ContentNegotiationStrategy strategy = new WebMvcAutoConfiguration.OptionalPathExtensionContentNegotiationStrategy( delegate); - MockHttpServletRequest request = new MockHttpServletRequest(); request.setAttribute( PathExtensionContentNegotiationStrategy.class.getName() + ".SKIP", diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index 3bd9ebff46..f51a650545 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -2600,7 +2600,7 @@ the artifact yourself instead of overriding the property. WARNING: Each Spring Boot release is designed and tested against this specific set of third-party dependencies. Overriding versions may cause compatibility issues. -To override dependency versions in Gradle, see {spring-boot-gradle-plugin-reference}/#managing-dependencies-customizing[ this section ] +To override dependency versions in Gradle, see {spring-boot-gradle-plugin-reference}/#managing-dependencies-customizing[this section] of the Gradle plugin's documentation. [[howto-create-an-executable-jar-with-maven]]