Skip to content

Commit

Permalink
RestClient support (#3930)
Browse files Browse the repository at this point in the history
Co-authored-by: Jacek Bilski <[email protected]>
  • Loading branch information
jacekbilski and Jacek Bilski authored Jan 17, 2025
1 parent ba95ef8 commit 7442e7b
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,23 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

Expand All @@ -53,6 +58,7 @@
import de.codecentric.boot.admin.client.registration.ReactiveRegistrationClient;
import de.codecentric.boot.admin.client.registration.RegistrationApplicationListener;
import de.codecentric.boot.admin.client.registration.RegistrationClient;
import de.codecentric.boot.admin.client.registration.RestClientRegistrationClient;
import de.codecentric.boot.admin.client.registration.ServletApplicationFactory;
import de.codecentric.boot.admin.client.registration.metadata.CompositeMetadataContributor;
import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
Expand All @@ -64,8 +70,8 @@
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@Conditional(SpringBootAdminClientEnabledCondition.class)
@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestTemplateAutoConfiguration.class,
WebClientAutoConfiguration.class })
@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestClientAutoConfiguration.class,
RestTemplateAutoConfiguration.class, WebClientAutoConfiguration.class })
@EnableConfigurationProperties({ ClientProperties.class, InstanceProperties.class, ServerProperties.class,
ManagementServerProperties.class })
public class SpringBootAdminClientAutoConfiguration {
Expand Down Expand Up @@ -152,9 +158,32 @@ public RegistrationClient registrationClient(ClientProperties client) {

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(RestClient.Builder.class)
public static class RestClientRegistrationClientConfig {

@Bean
@ConditionalOnMissingBean
public RegistrationClient registrationClient(ClientProperties client, RestClient.Builder restClientBuilder,
ClientHttpRequestFactoryBuilder<?> clientHttpRequestFactoryBuilder) {
var factorySettings = ClientHttpRequestFactorySettings.defaults()
.withConnectTimeout(client.getConnectTimeout())
.withReadTimeout(client.getReadTimeout());
var clientHttpRequestFactory = clientHttpRequestFactoryBuilder.build(factorySettings);
restClientBuilder = restClientBuilder.requestFactory(clientHttpRequestFactory);
if (client.getUsername() != null && client.getPassword() != null) {
restClientBuilder = restClientBuilder
.requestInterceptor(new BasicAuthenticationInterceptor(client.getUsername(), client.getPassword()));
}
var restClient = restClientBuilder.build();
return new RestClientRegistrationClient(restClient);
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(WebClient.Builder.class)
@ConditionalOnMissingBean(RestTemplateBuilder.class)
@ConditionalOnMissingBean({ RestTemplateBuilder.class, RestClient.Builder.class })
public static class ReactiveRegistrationClientConfig {

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ protected boolean register(Application application, String adminUrl, boolean fir
if (firstAttempt) {
LOGGER.warn(
"Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level",
application, this.adminUrls, ex.getMessage());
application, this.adminUrls, ex.getMessage(), ex);
}
else {
LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", application,
this.adminUrls, ex.getMessage());
this.adminUrls, ex.getMessage(), ex);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2014-2024 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 de.codecentric.boot.admin.client.registration;

import java.util.Collections;
import java.util.Map;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClient;

public class RestClientRegistrationClient implements RegistrationClient {

private static final ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<>() {
};

private final RestClient restClient;

public RestClientRegistrationClient(RestClient restClient) {
this.restClient = restClient;
}

@Override
public String register(String adminUrl, Application application) {
Map<String, Object> response = this.restClient.post()
.uri(adminUrl)
.headers(this::setRequestHeaders)
.body(application)
.retrieve()
.body(RESPONSE_TYPE);
return response.get("id").toString();
}

@Override
public void deregister(String adminUrl, String id) {
this.restClient.delete().uri(adminUrl + '/' + id).retrieve().toBodilessEntity();
}

protected void setRequestHeaders(HttpHeaders headers) {
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void should_default_autoDeregister_to_true() {
}

@Test
public void should_return_all_admiUrls() {
public void should_return_all_adminUrls() {
ClientProperties clientProperties = new ClientProperties();
clientProperties.setApiPath("register");
clientProperties.setUrl(new String[] { "http://first", "http://second" });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
Expand All @@ -34,6 +37,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;

import de.codecentric.boot.admin.client.registration.ApplicationRegistrator;
Expand Down Expand Up @@ -113,6 +117,32 @@ public void blockingClientInBlockingEnvironment() {
});
}

@Test
public void restClientRegistrationClientInBlockingEnvironment() {
WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner().withConfiguration(
AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, HttpClientAutoConfiguration.class,
RestClientAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class));

webApplicationContextRunner
.withPropertyValues("spring.boot.admin.client.url:http://localhost:8081",
"spring.boot.admin.client.connectTimeout=1337", "spring.boot.admin.client.readTimeout=42")
.withInitializer(new ConditionEvaluationReportLoggingListener())
.run((context) -> {
RegistrationClient registrationClient = context.getBean(RegistrationClient.class);
RestClient restClient = (RestClient) ReflectionTestUtils.getField(registrationClient, "restClient");
assertThat(restClient).isNotNull();

ClientHttpRequestFactory requestFactory = (ClientHttpRequestFactory) ReflectionTestUtils
.getField(restClient, "clientRequestFactory");

Integer connectTimeout = (Integer) ReflectionTestUtils.getField(requestFactory, "connectTimeout");
assertThat(connectTimeout).isEqualTo(1337);
Duration readTimeout = (Duration) ReflectionTestUtils.getField(requestFactory, "readTimeout");
assertThat(readTimeout).isEqualTo(Duration.ofMillis(42));
});
}

@Test
public void customBlockingClientInReactiveEnvironment() {
ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2014-2024 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 de.codecentric.boot.admin.client.registration;

import org.junit.jupiter.api.BeforeEach;
import org.springframework.web.client.RestClient;

public class RestClientRegistrationClientTest extends AbstractRegistrationClientTest {

@BeforeEach
public void setUp() {
super.setUp(new RestClientRegistrationClient(RestClient.create()));
}

}

0 comments on commit 7442e7b

Please sign in to comment.