diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateCustomizer.java b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateCustomizer.java index ac62e167..d271e566 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateCustomizer.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateCustomizer.java @@ -18,11 +18,13 @@ package org.springframework.vault.client; import org.springframework.web.client.RestTemplate; /** - * Callback interface that can be used to customize a {@link RestTemplate}. + * Callback interface that can be used to customize a {@link RestTemplate}. Beans + * implementing this interface are applied to + * {@link org.springframework.vault.client.RestTemplateBuilder}. * * @author Mark Paluch * @since 2.2 - * @see RestTemplateBuilder + * @see org.springframework.vault.client.RestTemplateBuilder#customizers(RestTemplateCustomizer...) */ @FunctionalInterface public interface RestTemplateCustomizer { diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateFactory.java new file mode 100644 index 00000000..20373119 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/RestTemplateFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.client; + +import java.util.function.Consumer; + +import org.springframework.web.client.RestTemplate; + +/** + * Factory interface that produces a {@link RestTemplate} object. Factory implementations + * are expected to create a new {@link RestTemplate} object when calling + * {@link #create()}. + * + * @author Mark Paluch + * @since 2.3 + * @see RestTemplateBuilder + * @see RestTemplate + */ +@FunctionalInterface +public interface RestTemplateFactory { + + /** + * Create a {@link RestTemplate} instance. + * + * @return a {@link RestTemplate} instance. + */ + default RestTemplate create() { + return create(builder -> { + }); + } + + /** + * Create a {@link RestTemplate} instance by applying {@code customizer} to the + * underlying {@link RestTemplateBuilder}. + * + * @param customizer builder customizer. + * @return a {@link RestTemplate} instance. + */ + RestTemplate create(Consumer customizer); +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientCustomizer.java b/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientCustomizer.java index 0e1e3db1..74fc943c 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientCustomizer.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientCustomizer.java @@ -18,11 +18,13 @@ package org.springframework.vault.client; import org.springframework.web.reactive.function.client.WebClient; /** - * Callback interface that can be used to customize a {@link WebClient.Builder}. + * Callback interface that can be used to customize a {@link WebClient.Builder}. Beans + * implementing this interface are applied to + * {@link org.springframework.vault.client.WebClientBuilder}. * * @author Mark Paluch * @since 2.2 - * @see WebClientBuilder + * @see org.springframework.vault.client.WebClientBuilder#customizers(WebClientCustomizer...) */ @FunctionalInterface public interface WebClientCustomizer { diff --git a/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientFactory.java new file mode 100644 index 00000000..205dd4da --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/client/WebClientFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.client; + +import java.util.function.Consumer; + +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Factory interface that produces a {@link WebClient} object. Factory implementations are + * expected to create a new {@link WebClient} object when calling {@link #create()}. + * + * @author Mark Paluch + * @since 2.3 + * @see WebClientBuilder + * @see WebClient + */ +@FunctionalInterface +public interface WebClientFactory { + + /** + * Create a {@link WebClient} instance. + * + * @return a {@link WebClient} instance. + */ + default WebClient create() { + return create(builder -> { + }); + } + + /** + * Create a {@link WebClient} instance by applying {@code customizer} to the + * underlying {@link WebClientBuilder}. + * + * @param customizer builder customizer. + * @return a {@link WebClient} instance. + */ + WebClient create(Consumer customizer); +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractReactiveVaultConfiguration.java b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractReactiveVaultConfiguration.java index 1a0fd40b..3abe5e81 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractReactiveVaultConfiguration.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractReactiveVaultConfiguration.java @@ -19,6 +19,7 @@ import java.time.Duration; import reactor.core.publisher.Mono; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.reactive.ClientHttpConnector; @@ -33,9 +34,11 @@ import org.springframework.vault.authentication.SessionManager; import org.springframework.vault.authentication.TokenAuthentication; import org.springframework.vault.authentication.VaultTokenSupplier; import org.springframework.vault.client.ClientHttpConnectorFactory; -import org.springframework.vault.client.ReactiveVaultClients; +import org.springframework.vault.client.VaultEndpoint; import org.springframework.vault.client.VaultEndpointProvider; import org.springframework.vault.client.WebClientBuilder; +import org.springframework.vault.client.WebClientCustomizer; +import org.springframework.vault.client.WebClientFactory; import org.springframework.vault.core.ReactiveVaultTemplate; import org.springframework.vault.support.ClientOptions; import org.springframework.vault.support.VaultToken; @@ -75,8 +78,33 @@ public abstract class AbstractReactiveVaultConfiguration */ protected WebClientBuilder webClientBuilder(VaultEndpointProvider endpointProvider, ClientHttpConnector httpConnector) { - return WebClientBuilder.builder().endpointProvider(endpointProvider) - .httpConnector(httpConnector); + + ObjectProvider customizers = getBeanFactory() + .getBeanProvider(WebClientCustomizer.class); + + WebClientBuilder builder = WebClientBuilder.builder() + .endpointProvider(endpointProvider).httpConnector(httpConnector); + + builder.customizers(customizers.stream().toArray(WebClientCustomizer[]::new)); + + return builder; + } + + /** + * Create a {@link WebClientFactory} bean that is used to produce a {@link WebClient}. + * + * @return the {@link WebClientFactory}. + * @see #clientHttpConnector() + * @since 2.3 + */ + @Bean + public WebClientFactory webClientFactory() { + + ClientHttpConnector httpConnector = clientHttpConnector(); + + return new DefaultWebClientFactory(httpConnector, clientHttpConnector -> { + return webClientBuilder(vaultEndpointProvider(), clientHttpConnector); + }); } /** @@ -89,8 +117,12 @@ public abstract class AbstractReactiveVaultConfiguration */ @Bean public ReactiveVaultTemplate reactiveVaultTemplate() { + + VaultEndpointProvider provider = vaultEndpointProvider(); + VaultEndpoint vaultEndpoint = provider.getVaultEndpoint(); + return new ReactiveVaultTemplate( - webClientBuilder(vaultEndpointProvider(), clientHttpConnector()), + webClientBuilder(() -> vaultEndpoint, clientHttpConnector()), getReactiveSessionManager()); } @@ -118,8 +150,8 @@ public abstract class AbstractReactiveVaultConfiguration @Bean public ReactiveSessionManager reactiveSessionManager() { - WebClient webClient = ReactiveVaultClients.createWebClient(vaultEndpoint(), - clientHttpConnector()); + WebClient webClient = getWebClientFactory().create(); + return new ReactiveLifecycleAwareSessionManager(vaultTokenSupplier(), getVaultThreadPoolTaskScheduler(), webClient); } @@ -147,8 +179,7 @@ public abstract class AbstractReactiveVaultConfiguration AuthenticationStepsFactory factory = (AuthenticationStepsFactory) clientAuthentication; - WebClient webClient = ReactiveVaultClients.createWebClient(vaultEndpoint(), - clientHttpConnector()); + WebClient webClient = getWebClientFactory().create(); AuthenticationStepsOperator stepsOperator = new AuthenticationStepsOperator( factory.getAuthenticationSteps(), webClient); @@ -173,6 +204,16 @@ public abstract class AbstractReactiveVaultConfiguration return ClientHttpConnectorFactory.create(clientOptions(), sslConfiguration()); } + /** + * Return the {@link WebClientFactory}. + * + * @return the {@link WebClientFactory} bean. + * @since 2.3 + */ + protected WebClientFactory getWebClientFactory() { + return getBeanFactory().getBean(WebClientFactory.class); + } + private ReactiveSessionManager getReactiveSessionManager() { return getBeanFactory().getBean("reactiveSessionManager", ReactiveSessionManager.class); diff --git a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java index 722eab73..bf9b7022 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/config/AbstractVaultConfiguration.java @@ -19,6 +19,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; @@ -33,6 +34,8 @@ import org.springframework.vault.authentication.LifecycleAwareSessionManager; import org.springframework.vault.authentication.SessionManager; import org.springframework.vault.client.ClientHttpRequestFactoryFactory; import org.springframework.vault.client.RestTemplateBuilder; +import org.springframework.vault.client.RestTemplateCustomizer; +import org.springframework.vault.client.RestTemplateFactory; import org.springframework.vault.client.SimpleVaultEndpointProvider; import org.springframework.vault.client.VaultEndpoint; import org.springframework.vault.client.VaultEndpointProvider; @@ -41,6 +44,7 @@ import org.springframework.vault.core.lease.SecretLeaseContainer; import org.springframework.vault.support.ClientOptions; import org.springframework.vault.support.SslConfiguration; import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; /** * Base class for Spring Vault configuration using JavaConfig. @@ -84,13 +88,40 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw * @return the {@link RestTemplateBuilder}. * @see #vaultEndpointProvider() * @see #clientHttpRequestFactoryWrapper() - * @since 2.2 + * @since 2.3 */ protected RestTemplateBuilder restTemplateBuilder( VaultEndpointProvider endpointProvider, ClientHttpRequestFactory requestFactory) { - return RestTemplateBuilder.builder().endpointProvider(endpointProvider) - .requestFactory(requestFactory); + + ObjectProvider customizers = getBeanFactory() + .getBeanProvider(RestTemplateCustomizer.class); + + RestTemplateBuilder builder = RestTemplateBuilder.builder() + .endpointProvider(endpointProvider).requestFactory(requestFactory); + + builder.customizers(customizers.stream().toArray(RestTemplateCustomizer[]::new)); + + return builder; + } + + /** + * Create a {@link RestTemplateFactory} bean that is used to produce + * {@link RestTemplate}. + * + * @return the {@link RestTemplateFactory}. + * @see #vaultEndpointProvider() + * @see #clientHttpRequestFactoryWrapper() + * @since 2.3 + */ + @Bean + public RestTemplateFactory restTemplateFactory( + ClientFactoryWrapper requestFactoryWrapper) { + + return new DefaultRestTemplateFactory( + requestFactoryWrapper.getClientHttpRequestFactory(), it -> { + return restTemplateBuilder(vaultEndpointProvider(), it); + }); } /** @@ -179,16 +210,14 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw /** * Construct a {@link RestOperations} object configured for Vault session management - * and authentication usage. Can be customized by overriding - * {@link #restTemplateBuilder(VaultEndpointProvider, ClientHttpRequestFactory)}. + * and authentication usage. Can be customized by providing a + * {@link RestTemplateFactory} bean. * * @return the {@link RestOperations} to be used for Vault access. - * @see #vaultEndpointProvider() - * @see #clientHttpRequestFactoryWrapper() + * @see #restTemplateFactory(ClientFactoryWrapper) */ public RestOperations restOperations() { - return restTemplateBuilder(vaultEndpointProvider(), - getClientFactoryWrapper().getClientHttpRequestFactory()).build(); + return getRestTemplateFactory().create(); } /** @@ -249,6 +278,16 @@ public abstract class AbstractVaultConfiguration implements ApplicationContextAw this.applicationContext = applicationContext; } + /** + * Return the {@link RestTemplateFactory}. + * + * @return the {@link RestTemplateFactory} bean. + * @since 2.3 + */ + protected RestTemplateFactory getRestTemplateFactory() { + return getBeanFactory().getBean(RestTemplateFactory.class); + } + BeanFactory getBeanFactory() { Assert.state(applicationContext != null, diff --git a/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultRestTemplateFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultRestTemplateFactory.java new file mode 100644 index 00000000..2fa92361 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultRestTemplateFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.config; + +import java.util.function.Consumer; +import java.util.function.Function; + +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.lang.Nullable; +import org.springframework.vault.client.RestTemplateBuilder; +import org.springframework.vault.client.RestTemplateFactory; +import org.springframework.web.client.RestTemplate; + +/** + * Default {@link RestTemplateFactory} implementation. + * + * @author Mark Paluch + */ +class DefaultRestTemplateFactory implements RestTemplateFactory { + + private final ClientHttpRequestFactory requestFactory; + private final Function builderFunction; + + DefaultRestTemplateFactory(ClientHttpRequestFactory requestFactory, + Function builderFunction) { + this.requestFactory = requestFactory; + this.builderFunction = builderFunction; + } + + @Override + public RestTemplate create(@Nullable Consumer customizer) { + + RestTemplateBuilder builder = builderFunction.apply(requestFactory); + + if (customizer != null) { + customizer.accept(builder); + } + + return builder.build(); + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultWebClientFactory.java b/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultWebClientFactory.java new file mode 100644 index 00000000..e7051e69 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/config/DefaultWebClientFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.config; + +import java.util.function.Consumer; +import java.util.function.Function; + +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.lang.Nullable; +import org.springframework.vault.client.WebClientBuilder; +import org.springframework.vault.client.WebClientFactory; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Default implementation of {@link WebClientFactory}. + * + * @author Mark Paluch + * @since 2.3 + */ +class DefaultWebClientFactory implements WebClientFactory { + + private final ClientHttpConnector connector; + private final Function builderFunction; + + DefaultWebClientFactory(ClientHttpConnector connector, + Function builderFunction) { + this.connector = connector; + this.builderFunction = builderFunction; + } + + @Override + public WebClient create(@Nullable Consumer customizer) { + + WebClientBuilder builder = builderFunction.apply(connector); + + if (customizer != null) { + customizer.accept(builder); + } + + return builder.build(); + } +} diff --git a/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractReactiveVaultConfigurationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractReactiveVaultConfigurationUnitTests.java new file mode 100644 index 00000000..c2c1e86d --- /dev/null +++ b/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractReactiveVaultConfigurationUnitTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.config; + +import org.junit.jupiter.api.Test; +import reactor.test.StepVerifier; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.client.WebClientCustomizer; +import org.springframework.vault.client.WebClientFactory; +import org.springframework.vault.core.ReactiveVaultOperations; +import org.springframework.vault.support.SslConfiguration; +import org.springframework.vault.util.Settings; +import org.springframework.vault.util.TestRestTemplateFactory; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Integration tests for {@link AbstractReactiveVaultConfiguration}. + * + * @author Mark Paluch + */ +class AbstractReactiveVaultConfigurationUnitTests { + + @Test + void shouldApplyCustomizerToWebClientFactory() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WebClientCustomizerConfiguration.class); + + WebClientFactory factory = context.getBean(WebClientFactory.class); + WebClient webClient = factory.create(); + + webClient.get().uri("/foo").exchange().as(StepVerifier::create) + .verifyError(CustomizedSignal.class); + } + + @Test + void shouldApplyCustomizerToTemplate() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WebClientCustomizerConfiguration.class); + + ReactiveVaultOperations operations = context + .getBean(ReactiveVaultOperations.class); + + operations.read("/foo").as(StepVerifier::create) + .verifyError(CustomizedSignal.class); + } + + @Configuration(proxyBeanMethods = false) + static class WebClientCustomizerConfiguration + extends AbstractReactiveVaultConfiguration { + + @Override + public VaultEndpoint vaultEndpoint() { + return TestRestTemplateFactory.TEST_VAULT_ENDPOINT; + } + + @Override + public ClientAuthentication clientAuthentication() { + return new TokenAuthentication(Settings.token()); + } + + @Override + public SslConfiguration sslConfiguration() { + return Settings.createSslConfiguration(); + } + + @Bean + public WebClientCustomizer customizer() { + return builder -> builder.exchangeFunction(request -> { + throw new CustomizedSignal(); + }); + } + } + + static class CustomizedSignal extends RuntimeException { + + } +} diff --git a/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractVaultConfigurationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractVaultConfigurationUnitTests.java new file mode 100644 index 00000000..f0fad430 --- /dev/null +++ b/spring-vault-core/src/test/java/org/springframework/vault/config/AbstractVaultConfigurationUnitTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 2020 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 + * + * https://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.vault.config; + +import org.junit.jupiter.api.Test; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.vault.authentication.ClientAuthentication; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.RestTemplateCustomizer; +import org.springframework.vault.client.RestTemplateFactory; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.support.SslConfiguration; +import org.springframework.vault.util.Settings; +import org.springframework.vault.util.TestRestTemplateFactory; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/** + * Integration tests for {@link AbstractVaultConfiguration}. + * + * @author Mark Paluch + */ +class AbstractVaultConfigurationUnitTests { + + @Test + void shouldApplyCustomizerToRestTemplateFactory() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + RestTemplateCustomizerConfiguration.class); + + RestTemplateFactory factory = context.getBean(RestTemplateFactory.class); + RestTemplate restTemplate = factory.create(); + + assertThatExceptionOfType(CustomizedSignal.class) + .isThrownBy(() -> restTemplate.delete("/foo")); + } + + @Test + void shouldApplyCustomizerToTemplate() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + RestTemplateCustomizerConfiguration.class); + + VaultOperations operations = context.getBean(VaultOperations.class); + + assertThatExceptionOfType(CustomizedSignal.class) + .isThrownBy(() -> operations.opsForSys().health()); + } + + @Configuration(proxyBeanMethods = false) + static class RestTemplateCustomizerConfiguration extends AbstractVaultConfiguration { + + @Override + public VaultEndpoint vaultEndpoint() { + return TestRestTemplateFactory.TEST_VAULT_ENDPOINT; + } + + @Override + public ClientAuthentication clientAuthentication() { + return new TokenAuthentication(Settings.token()); + } + + @Override + public SslConfiguration sslConfiguration() { + return Settings.createSslConfiguration(); + } + + @Bean + public RestTemplateCustomizer customizer() { + return restTemplate -> restTemplate + .setRequestFactory((ClientHttpRequestFactory) (uri, httpMethod) -> { + throw new CustomizedSignal(); + }); + } + } + + static class CustomizedSignal extends RuntimeException { + + } +}