diff --git a/.editorconfig b/.editorconfig index 4e7ca1f9d4f..eab962a52cc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,7 +23,7 @@ ij_java_blank_lines_after_class_header = 0 ij_java_doc_enable_formatting = false ij_java_class_count_to_use_import_on_demand = 100 ij_java_names_count_to_use_import_on_demand = 100 -ij_java_imports_layout = |, java.**, |, jakarta.**, *, |, de.codecentric.boot.admin.**, |, $* +ij_java_imports_layout = |, java.**, |, jakarta.**, *, tools.**, |, de.codecentric.boot.admin.**, |, $* ij_java_layout_static_imports_separately = true [*.xml] diff --git a/.gitignore b/.gitignore index ef7d5b97a97..d42edd459f9 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ node/ .DS_Store mockServiceWorker.js +/.github/agents/ diff --git a/pom.xml b/pom.xml index aec66958c83..c6cb22ece42 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ https://github.com/codecentric/spring-boot-admin/ - 3.5.8-SNAPSHOT + 4.0.0-SNAPSHOT 17 v22.12.0 @@ -47,10 +47,11 @@ true - 3.5.10 - 2025.0.0 + 4.0.2 + 2025.1.1 - 2.4.3 + + 2.1.0 12.3.1 3.0.2 @@ -58,7 +59,6 @@ 3.13.2 5.6.0 4.3.0 - 2.0.3 12.1.6 diff --git a/spring-boot-admin-build/pom.xml b/spring-boot-admin-build/pom.xml index dd04572d055..a150702b705 100644 --- a/spring-boot-admin-build/pom.xml +++ b/spring-boot-admin-build/pom.xml @@ -85,18 +85,6 @@ ${awaitility.version} test - - org.testcontainers - testcontainers - ${testcontainers.version} - test - - - org.testcontainers - testcontainers-junit-jupiter - ${testcontainers.version} - test - diff --git a/spring-boot-admin-client/pom.xml b/spring-boot-admin-client/pom.xml index 1a5334c620f..ead3f1533f8 100644 --- a/spring-boot-admin-client/pom.xml +++ b/spring-boot-admin-client/pom.xml @@ -41,27 +41,27 @@ spring-boot-starter-actuator - org.springframework - spring-web + org.springframework.boot + spring-boot-starter-restclient org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webflux true org.springframework.boot - spring-boot-autoconfigure-processor + spring-boot-starter-webmvc true org.springframework.boot - spring-boot-configuration-processor + spring-boot-autoconfigure-processor true - org.springframework - spring-webflux + org.springframework.boot + spring-boot-configuration-processor true @@ -74,11 +74,6 @@ spring-boot-starter-test test - - org.springframework.boot - spring-boot-starter-webflux - test - org.wiremock wiremock-standalone diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientProperties.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientProperties.java index de378ce6360..dc55dcddc2d 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientProperties.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientProperties.java @@ -19,11 +19,11 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; +import org.jspecify.annotations.Nullable; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; import org.springframework.core.env.Environment; -import org.springframework.lang.Nullable; @lombok.Data @ConfigurationProperties(prefix = "spring.boot.admin.client") @@ -60,21 +60,18 @@ public class ClientProperties { /** * Username for basic authentication on admin server */ - @Nullable - private String username; + @Nullable private String username; /** * Password for basic authentication on admin server */ - @Nullable - private String password; + @Nullable private String password; /** * Enable automatic deregistration on shutdown If not set it defaults to true if an * active {@link CloudPlatform} is present; */ - @Nullable - private Boolean autoDeregistration = null; + @Nullable private Boolean autoDeregistration = null; /** * Enable automatic registration when the application is ready. diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientRuntimeHints.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientRuntimeHints.java index b8f5ffaf2f8..67d4efea819 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientRuntimeHints.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/ClientRuntimeHints.java @@ -21,7 +21,7 @@ import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.boot.web.server.context.WebServerInitializedEvent; import org.springframework.context.annotation.Configuration; import de.codecentric.boot.admin.client.registration.Application; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/CloudFoundryApplicationProperties.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/CloudFoundryApplicationProperties.java index 4f376a22680..4fa321c701d 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/CloudFoundryApplicationProperties.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/CloudFoundryApplicationProperties.java @@ -19,18 +19,16 @@ import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.lang.Nullable; @lombok.Data @ConfigurationProperties("vcap.application") public class CloudFoundryApplicationProperties { - @Nullable - private String applicationId; + @Nullable private String applicationId; - @Nullable - private String instanceIndex; + @Nullable private String instanceIndex; private List uris = new ArrayList<>(); diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/InstanceProperties.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/InstanceProperties.java index 3d1a49cd921..4a6e63c42c7 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/InstanceProperties.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/InstanceProperties.java @@ -19,9 +19,9 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.lang.Nullable; @lombok.Data @ConfigurationProperties(prefix = "spring.boot.admin.client.instance") @@ -31,43 +31,37 @@ public class InstanceProperties { * Management-url to register with. Inferred at runtime, can be overridden in case the * reachable URL is different (e.g. Docker). */ - @Nullable - private String managementUrl; + @Nullable private String managementUrl; /** * Base url for computing the management-url to register with. The path is inferred at * runtime, and appended to the base url. */ - @Nullable - private String managementBaseUrl; + @Nullable private String managementBaseUrl; /** * Client-service-URL register with. Inferred at runtime, can be overridden in case * the reachable URL is different (e.g. Docker). */ - @Nullable - private String serviceUrl; + @Nullable private String serviceUrl; /** * Base url for computing the service-url to register with. The path is inferred at * runtime, and appended to the base url. */ - @Nullable - private String serviceBaseUrl; + @Nullable private String serviceBaseUrl; /** * Path for computing the service-url to register with. If not specified, defaults to * "/" */ - @Nullable - private String servicePath; + @Nullable private String servicePath; /** * Client-health-URL to register with. Inferred at runtime, can be overridden in case * the reachable URL is different (e.g. Docker). Must be unique all services registry. */ - @Nullable - private String healthUrl; + @Nullable private String healthUrl; /** * Name to register with. Defaults to ${spring.application.name} diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java index 1c361bf02dd..75e609dc351 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java @@ -29,33 +29,29 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.condition.ConditionalOnWebApplication.Type; 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.boot.http.client.HttpClientSettings; +import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.webflux.autoconfigure.WebFluxProperties; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath; 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.http.converter.json.JacksonJsonHttpMessageConverter; import org.springframework.web.client.RestClient; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.client.WebClient; +import tools.jackson.databind.json.JsonMapper; import de.codecentric.boot.admin.client.registration.ApplicationFactory; import de.codecentric.boot.admin.client.registration.ApplicationRegistrator; -import de.codecentric.boot.admin.client.registration.BlockingRegistrationClient; import de.codecentric.boot.admin.client.registration.DefaultApplicationRegistrator; import de.codecentric.boot.admin.client.registration.ReactiveApplicationFactory; -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; @@ -64,14 +60,10 @@ import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor; import de.codecentric.boot.admin.client.registration.metadata.StartupDateMetadataContributor; -import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication; - @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @Conditional(SpringBootAdminClientEnabledCondition.class) -@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestClientAutoConfiguration.class, - RestTemplateAutoConfiguration.class, WebClientAutoConfiguration.class }) +@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestClientAutoConfiguration.class }) @EnableConfigurationProperties({ ClientProperties.class, InstanceProperties.class, ServerProperties.class, ManagementServerProperties.class }) public class SpringBootAdminClientAutoConfiguration { @@ -139,59 +131,33 @@ public ApplicationFactory applicationFactory(InstanceProperties instance, Manage } @Configuration(proxyBeanMethods = false) - @ConditionalOnBean(RestTemplateBuilder.class) - public static class BlockingRegistrationClientConfig { - - @Bean - @ConditionalOnMissingBean - public RegistrationClient registrationClient(ClientProperties client) { - RestTemplateBuilder builder = new RestTemplateBuilder().connectTimeout(client.getConnectTimeout()) - .readTimeout(client.getReadTimeout()); - - if (client.getUsername() != null && client.getPassword() != null) { - builder = builder.basicAuthentication(client.getUsername(), client.getPassword()); - } - - RestTemplate build = builder.build(); - return new BlockingRegistrationClient(build); - } - - } - - @Configuration(proxyBeanMethods = false) - @ConditionalOnBean({ RestClient.Builder.class, ClientHttpRequestFactoryBuilder.class }) + @ConditionalOnBean(RestClient.Builder.class) public static class RestClientRegistrationClientConfig { @Bean @ConditionalOnMissingBean public RegistrationClient registrationClient(ClientProperties client, RestClient.Builder restClientBuilder, - ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder) { - var factorySettings = ClientHttpRequestFactorySettings.defaults() + ObjectProvider objectMapper) { + var factorySettings = HttpClientSettings.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); - } - } + var clientHttpRequestFactory = ClientHttpRequestFactoryBuilder.detect().build(factorySettings); - @Configuration(proxyBeanMethods = false) - @ConditionalOnBean(WebClient.Builder.class) - public static class ReactiveRegistrationClientConfig { + restClientBuilder.requestFactory(clientHttpRequestFactory); + + objectMapper.ifAvailable((mapper) -> restClientBuilder.messageConverters((configurer) -> { + configurer.removeIf(JacksonJsonHttpMessageConverter.class::isInstance); + configurer.add(new JacksonJsonHttpMessageConverter(mapper)); + })); - @Bean - @ConditionalOnMissingBean - public RegistrationClient registrationClient(ClientProperties client, WebClient.Builder webClient) { if (client.getUsername() != null && client.getPassword() != null) { - webClient = webClient.filter(basicAuthentication(client.getUsername(), client.getPassword())); + restClientBuilder + .requestInterceptor(new BasicAuthenticationInterceptor(client.getUsername(), client.getPassword())); } - return new ReactiveRegistrationClient(webClient.build(), client.getReadTimeout()); + + var restClient = restClientBuilder.build(); + return new RestClientRegistrationClient(restClient); } } diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfiguration.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfiguration.java index a9326ab20fd..1e062e30bb5 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfiguration.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfiguration.java @@ -27,9 +27,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; 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.cloud.CloudPlatform; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringNativeClientAutoConfiguration.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringNativeClientAutoConfiguration.java index 198a9a2a843..c171328a768 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringNativeClientAutoConfiguration.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringNativeClientAutoConfiguration.java @@ -19,8 +19,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; +import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportRuntimeHints; @@ -28,8 +27,7 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @Conditional(SpringBootAdminClientEnabledCondition.class) -@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestTemplateAutoConfiguration.class, - WebClientAutoConfiguration.class }) +@AutoConfigureAfter({ WebEndpointAutoConfiguration.class, RestClientAutoConfiguration.class }) @ImportRuntimeHints({ ClientRuntimeHints.class }) public class SpringNativeClientAutoConfiguration { diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/package-info.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/package-info.java index 9ac974012b4..1600cf8c7dc 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/package-info.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/package-info.java @@ -14,9 +14,7 @@ * limitations under the License. */ -@NonNullApi -@NonNullFields +@NullMarked package de.codecentric.boot.admin.client.config; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClient.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClient.java deleted file mode 100644 index a34b61bcba5..00000000000 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClient.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014-2023 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.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -public class BlockingRegistrationClient implements RegistrationClient { - - private static final ParameterizedTypeReference> RESPONSE_TYPE = new ParameterizedTypeReference<>() { - }; - - private final RestTemplate restTemplate; - - public BlockingRegistrationClient(RestTemplate restTemplate) { - this.restTemplate = restTemplate; - } - - @Override - public String register(String adminUrl, Application application) { - ResponseEntity> response = this.restTemplate.exchange(adminUrl, HttpMethod.POST, - new HttpEntity<>(application, this.createRequestHeaders()), RESPONSE_TYPE); - return response.getBody().get("id").toString(); - } - - @Override - public void deregister(String adminUrl, String id) { - this.restTemplate.delete(adminUrl + '/' + id); - } - - protected HttpHeaders createRequestHeaders() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - return HttpHeaders.readOnlyHttpHeaders(headers); - } - -} diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactory.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactory.java index bdab8c466ea..07f337fb1c0 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactory.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactory.java @@ -19,7 +19,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; import org.springframework.util.StringUtils; import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactory.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactory.java index 4efe51cfd5b..93f1669ec3f 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactory.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactory.java @@ -21,15 +21,15 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.web.server.context.WebServerInitializedEvent; import org.springframework.context.event.EventListener; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; @@ -57,11 +57,9 @@ public class DefaultApplicationFactory implements ApplicationFactory { private final MetadataContributor metadataContributor; - @Nullable - private Integer localServerPort; + @Nullable private Integer localServerPort; - @Nullable - private Integer localManagementPort; + @Nullable private Integer localManagementPort; public DefaultApplicationFactory(InstanceProperties instance, ManagementServerProperties management, ServerProperties server, PathMappedEndpoints pathMappedEndpoints, WebEndpointProperties webEndpoint, diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactory.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactory.java index 1c65f63d2f2..6dd86c18440 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactory.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactory.java @@ -19,9 +19,9 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.webflux.autoconfigure.WebFluxProperties; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClient.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClient.java deleted file mode 100644 index d622b952f21..00000000000 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClient.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014-2023 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.time.Duration; -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.reactive.function.client.WebClient; - -public class ReactiveRegistrationClient implements RegistrationClient { - - private static final ParameterizedTypeReference> RESPONSE_TYPE = new ParameterizedTypeReference<>() { - }; - - private final WebClient webclient; - - private final Duration timeout; - - public ReactiveRegistrationClient(WebClient webclient, Duration timeout) { - this.webclient = webclient; - this.timeout = timeout; - } - - @Override - public String register(String adminUrl, Application application) { - Map response = this.webclient.post() - .uri(adminUrl) - .headers(this::setRequestHeaders) - .bodyValue(application) - .retrieve() - .bodyToMono(RESPONSE_TYPE) - .timeout(this.timeout) - .block(); - return response.get("id").toString(); - } - - @Override - public void deregister(String adminUrl, String id) { - this.webclient.delete().uri(adminUrl + '/' + id).retrieve().toBodilessEntity().timeout(this.timeout).block(); - } - - protected void setRequestHeaders(HttpHeaders headers) { - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - } - -} diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/RegistrationApplicationListener.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/RegistrationApplicationListener.java index c226f10027b..35fd9c5e277 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/RegistrationApplicationListener.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/RegistrationApplicationListener.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.concurrent.ScheduledFuture; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; @@ -28,7 +29,6 @@ import org.springframework.context.event.EventListener; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.lang.Nullable; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** @@ -51,13 +51,17 @@ public class RegistrationApplicationListener implements InitializingBean, Dispos private Duration registerPeriod = Duration.ofSeconds(10); - @Nullable - private volatile ScheduledFuture scheduledTask; + @Nullable private volatile ScheduledFuture scheduledTask; public RegistrationApplicationListener(ApplicationRegistrator registrator) { this(registrator, registrationTaskScheduler()); } + RegistrationApplicationListener(ApplicationRegistrator registrator, ThreadPoolTaskScheduler taskScheduler) { + this.registrator = registrator; + this.taskScheduler = taskScheduler; + } + private static ThreadPoolTaskScheduler registrationTaskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(1); @@ -66,11 +70,6 @@ private static ThreadPoolTaskScheduler registrationTaskScheduler() { return taskScheduler; } - RegistrationApplicationListener(ApplicationRegistrator registrator, ThreadPoolTaskScheduler taskScheduler) { - this.registrator = registrator; - this.taskScheduler = taskScheduler; - } - @EventListener @Order(Ordered.LOWEST_PRECEDENCE) public void onApplicationReady(ApplicationReadyEvent event) { diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactory.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactory.java index 0c867c963d4..3873418f2d3 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactory.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactory.java @@ -20,9 +20,9 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponentsBuilder; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/metadata/package-info.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/metadata/package-info.java index 922ad64db5c..2a50faa7601 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/metadata/package-info.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/metadata/package-info.java @@ -14,9 +14,7 @@ * limitations under the License. */ -@NonNullApi -@NonNullFields +@NullMarked package de.codecentric.boot.admin.client.registration.metadata; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/package-info.java b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/package-info.java index e332f2b2c6c..bbba736f6d5 100644 --- a/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/package-info.java +++ b/spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/package-info.java @@ -14,9 +14,7 @@ * limitations under the License. */ -@NonNullApi -@NonNullFields +@NullMarked package de.codecentric.boot.admin.client.registration; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfigurationTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfigurationTest.java index 8b53562256d..1041524e2be 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfigurationTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfigurationTest.java @@ -22,26 +22,19 @@ 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; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.context.annotation.UserConfigurations; +import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration; +import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.webflux.autoconfigure.WebFluxProperties; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; 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; -import de.codecentric.boot.admin.client.registration.BlockingRegistrationClient; import de.codecentric.boot.admin.client.registration.RegistrationClient; import static org.assertj.core.api.Assertions.assertThat; @@ -50,8 +43,8 @@ class SpringBootAdminClientAutoConfigurationTest { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, RestTemplateAutoConfiguration.class, - SpringBootAdminClientAutoConfiguration.class)); + DispatcherServletAutoConfiguration.class, HttpClientAutoConfiguration.class, + RestClientAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class)); @Test void not_active() { @@ -84,39 +77,14 @@ void nonWebEnvironment() { @Test void reactiveEnvironment() { ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner() - .withConfiguration( - AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, - WebClientAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, + WebEndpointAutoConfiguration.class, HttpClientAutoConfiguration.class, + RestClientAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class)) .withBean(WebFluxProperties.class); reactiveContextRunner.withPropertyValues("spring.boot.admin.client.url:http://localhost:8081") .run((context) -> assertThat(context).hasSingleBean(ApplicationRegistrator.class)); } - @Test - void blockingClientInBlockingEnvironment() { - WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, - WebEndpointAutoConfiguration.class, DispatcherServletAutoConfiguration.class, - RestTemplateAutoConfiguration.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") - .run((context) -> { - RegistrationClient registrationClient = context.getBean(RegistrationClient.class); - RestTemplate restTemplate = (RestTemplate) ReflectionTestUtils.getField(registrationClient, - "restTemplate"); - assertThat(restTemplate).isNotNull(); - - ClientHttpRequestFactory requestFactory = restTemplate.getRequestFactory(); - - 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 void restClientRegistrationClientInBlockingEnvironment() { WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner().withConfiguration( @@ -143,49 +111,4 @@ void restClientRegistrationClientInBlockingEnvironment() { }); } - @Test - void customBlockingClientInReactiveEnvironment() { - ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner() - .withConfiguration(UserConfigurations.of(CustomBlockingConfiguration.class)) - .withConfiguration( - AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, - WebClientAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class)) - .withBean(WebFluxProperties.class); - - reactiveContextRunner.withPropertyValues("spring.boot.admin.client.url:http://localhost:8081") - .run((context) -> { - assertThat(context).hasSingleBean(ApplicationRegistrator.class); - assertThat(context).getBean("registrationClient") - .isEqualTo(context.getBean(CustomBlockingConfiguration.class).registrationClient); - }); - } - - @Test - void customBlockingClientInBlockingEnvironment() { - WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner() - .withConfiguration(UserConfigurations.of(CustomBlockingConfiguration.class)) - .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, - WebEndpointAutoConfiguration.class, DispatcherServletAutoConfiguration.class, - RestTemplateAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class)); - - webApplicationContextRunner.withPropertyValues("spring.boot.admin.client.url:http://localhost:8081") - .run((context) -> { - assertThat(context).hasSingleBean(ApplicationRegistrator.class); - assertThat(context).getBean("registrationClient") - .isEqualTo(context.getBean(CustomBlockingConfiguration.class).registrationClient); - }); - } - - @Configuration - public static class CustomBlockingConfiguration { - - final RegistrationClient registrationClient = new BlockingRegistrationClient(new RestTemplate()); - - @Bean - public RegistrationClient registrationClient() { - return registrationClient; - } - - } - } diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfigurationTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfigurationTest.java index 9dfc12b08f7..f7b31be1f25 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfigurationTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientCloudFoundryAutoConfigurationTest.java @@ -20,10 +20,11 @@ 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.web.client.RestTemplateAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; +import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration; +import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; +import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration; import de.codecentric.boot.admin.client.registration.ApplicationFactory; import de.codecentric.boot.admin.client.registration.CloudFoundryApplicationFactory; @@ -37,7 +38,8 @@ class SpringBootAdminClientCloudFoundryAutoConfigurationTest { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class, - RestTemplateAutoConfiguration.class, SpringBootAdminClientAutoConfiguration.class, + HttpClientAutoConfiguration.class, RestClientAutoConfiguration.class, + SpringBootAdminClientAutoConfiguration.class, SpringBootAdminClientCloudFoundryAutoConfiguration.class)); @Test diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientRegistrationClientAutoConfigurationTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientRegistrationClientAutoConfigurationTest.java index cce6ba8086c..d52af7bc8e5 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientRegistrationClientAutoConfigurationTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientRegistrationClientAutoConfigurationTest.java @@ -26,15 +26,11 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; import org.springframework.web.client.RestClient; -import org.springframework.web.reactive.function.client.WebClient; -import de.codecentric.boot.admin.client.registration.BlockingRegistrationClient; -import de.codecentric.boot.admin.client.registration.ReactiveRegistrationClient; import de.codecentric.boot.admin.client.registration.RegistrationClient; import de.codecentric.boot.admin.client.registration.RestClientRegistrationClient; @@ -46,7 +42,7 @@ public class SpringBootAdminClientRegistrationClientAutoConfigurationTest { @MethodSource("contextRunnerCustomizations") void autoConfiguresRegistrationClient(String testCaseName, Function customizer, - Class expectedRegistrationClient) { + Class expectedRegistrationClient) { WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner() .withConfiguration( AutoConfigurations.of(EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, @@ -62,107 +58,21 @@ void autoConfiguresRegistrationClient(String testCaseName, }); } - /** - * - * @return context runner customizations - */ public static Stream contextRunnerCustomizations() { return Stream.of(// Arguments.of(// - "Test case 01", // + "RestClientBuilder with ClientHttpRequestFactoryBuilder", // customizer() // - .withRestTemplateBuilder() - .withRestClientBuilder() - .withClientHttpRequestFactoryBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 02", // - customizer() // - .withRestTemplateBuilder() .withRestClientBuilder() .withClientHttpRequestFactoryBuilder() .build(), // RestClientRegistrationClient.class), Arguments.of(// - "Test case 03", // + "RestClientBuilder only", // customizer() // - .withRestTemplateBuilder() .withRestClientBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 04", // - customizer() // - .withRestTemplateBuilder() - .withRestClientBuilder() - .build(), // - BlockingRegistrationClient.class), - Arguments.of(// - "Test case 05", // - customizer() // - .withRestTemplateBuilder() - .withClientHttpRequestFactoryBuilder() - .withWebClientBuilder() .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 06", // - customizer() // - .withRestTemplateBuilder() - .withClientHttpRequestFactoryBuilder() - .build(), // - BlockingRegistrationClient.class), - Arguments.of(// - "Test case 07", // - customizer() // - .withRestTemplateBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 08", // - customizer() // - .withRestTemplateBuilder() - .build(), // - BlockingRegistrationClient.class), - Arguments.of(// - "Test case 09", // - customizer() // - .withRestClientBuilder() - .withClientHttpRequestFactoryBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 10", // - customizer() // - .withRestClientBuilder() - .withClientHttpRequestFactoryBuilder() - .build(), // - RestClientRegistrationClient.class), - Arguments.of(// - "Test case 11", // - customizer() // - .withRestClientBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 13", // - customizer() // - .withClientHttpRequestFactoryBuilder() - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class), - Arguments.of(// - "Test case 15", // - customizer() // - .withWebClientBuilder() - .build(), // - ReactiveRegistrationClient.class) // + RestClientRegistrationClient.class) // ); } @@ -174,12 +84,6 @@ private static final class ContextRunnerCustomizerBuilder { private Function customizer = (runner) -> runner; - ContextRunnerCustomizerBuilder withRestTemplateBuilder() { - customizer = customizer - .andThen((runner) -> runner.withBean(RestTemplateBuilder.class, RestTemplateBuilder::new)); - return this; - } - ContextRunnerCustomizerBuilder withRestClientBuilder() { customizer = customizer.andThen((runner) -> runner.withBean(RestClient.Builder.class, RestClient::builder)); return this; @@ -191,11 +95,6 @@ ContextRunnerCustomizerBuilder withClientHttpRequestFactoryBuilder() { return this; } - ContextRunnerCustomizerBuilder withWebClientBuilder() { - customizer = customizer.andThen((runner) -> runner.withBean(WebClient.Builder.class, WebClient::builder)); - return this; - } - Function build() { return customizer; } diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ApplicationTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ApplicationTest.java index 8bb18054bfa..91f1adff66c 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ApplicationTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ApplicationTest.java @@ -16,21 +16,18 @@ package de.codecentric.boot.admin.client.registration; -import java.io.IOException; - -import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; import org.junit.jupiter.api.Test; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import tools.jackson.databind.json.JsonMapper; import static org.assertj.core.api.Assertions.assertThat; class ApplicationTest { @Test - void test_json_format() throws IOException { - ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build(); + void test_json_format() { + JsonMapper jsonMapper = JsonMapper.builder().build(); Application app = Application.create("test") .healthUrl("http://health") @@ -38,7 +35,7 @@ void test_json_format() throws IOException { .managementUrl("http://management") .build(); - DocumentContext json = JsonPath.parse(objectMapper.writeValueAsString(app)); + DocumentContext json = JsonPath.parse(jsonMapper.writeValueAsString(app)); assertThat((String) json.read("$.name")).isEqualTo("test"); assertThat((String) json.read("$.serviceUrl")).isEqualTo("http://service"); diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClientTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClientTest.java deleted file mode 100644 index cd891f6042a..00000000000 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/BlockingRegistrationClientTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2014-2023 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.RestTemplate; - -class BlockingRegistrationClientTest extends AbstractRegistrationClientTest { - - @BeforeEach - void setUp() { - super.setUp(new BlockingRegistrationClient(new RestTemplate())); - } - -} diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactoryTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactoryTest.java index c0fcae7c33f..995e8868ab9 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactoryTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/CloudFoundryApplicationFactoryTest.java @@ -23,7 +23,7 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties; import de.codecentric.boot.admin.client.config.InstanceProperties; diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactoryTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactoryTest.java index 512ac881273..bc698ac4ca7 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactoryTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactoryTest.java @@ -25,11 +25,11 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.web.context.WebServerApplicationContext; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.web.server.context.WebServerApplicationContext; +import org.springframework.boot.web.server.context.WebServerInitializedEvent; import de.codecentric.boot.admin.client.config.InstanceProperties; diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactoryTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactoryTest.java index d27d768eb3c..195d82d0f77 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactoryTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveApplicationFactoryTest.java @@ -25,11 +25,11 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; -import org.springframework.boot.web.context.WebServerApplicationContext; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.web.server.context.WebServerApplicationContext; +import org.springframework.boot.web.server.context.WebServerInitializedEvent; +import org.springframework.boot.webflux.autoconfigure.WebFluxProperties; import de.codecentric.boot.admin.client.config.InstanceProperties; diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClientTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClientTest.java deleted file mode 100644 index 15fbd54bc07..00000000000 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ReactiveRegistrationClientTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014-2023 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.time.Duration; - -import org.junit.jupiter.api.BeforeEach; -import org.springframework.web.reactive.function.client.WebClient; - -class ReactiveRegistrationClientTest extends AbstractRegistrationClientTest { - - @BeforeEach - void setUp() { - super.setUp(new ReactiveRegistrationClient(WebClient.create(), Duration.ofSeconds(10))); - } - -} diff --git a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactoryTest.java b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactoryTest.java index 24351d67aef..0522ca48fcc 100644 --- a/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactoryTest.java +++ b/spring-boot-admin-client/src/test/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactoryTest.java @@ -26,11 +26,11 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; -import org.springframework.boot.web.context.WebServerApplicationContext; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.server.autoconfigure.ServerProperties; +import org.springframework.boot.web.server.context.WebServerApplicationContext; +import org.springframework.boot.web.server.context.WebServerInitializedEvent; +import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath; import org.springframework.mock.web.MockServletContext; import de.codecentric.boot.admin.client.config.InstanceProperties; diff --git a/spring-boot-admin-docs/src/site/docs/installation-and-setup/index.md b/spring-boot-admin-docs/src/site/docs/installation-and-setup/index.md index a4bb77de72a..27b84a59dc2 100644 --- a/spring-boot-admin-docs/src/site/docs/installation-and-setup/index.md +++ b/spring-boot-admin-docs/src/site/docs/installation-and-setup/index.md @@ -40,7 +40,7 @@ Boot Starter. In this example, we'll use the Servlet Web Starter. ### Setting up the Spring Boot Admin Server To set up Spring Boot Admin Server, you need to add the dependency `spring-boot-admin-starter-server` as well as -`spring-boot-starter-web` to your project (either in your `pom.xml` or `build.gradle(.kts)`). +`spring-boot-starter-webmvc` to your project (either in your `pom.xml` or `build.gradle(.kts)`). ```xml title="pom.xml" @@ -52,7 +52,7 @@ To set up Spring Boot Admin Server, you need to add the dependency `spring-boot- org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc ``` diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-consul/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-consul/pom.xml index ba4cfae5da7..172ce9b43f4 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-consul/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-consul/pom.xml @@ -34,7 +34,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-eureka/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-eureka/pom.xml index 579660dc349..08110f422a4 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-eureka/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-eureka/pom.xml @@ -38,7 +38,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-hazelcast/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-hazelcast/pom.xml index 146b0af2b5d..36f37079390 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-hazelcast/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-hazelcast/pom.xml @@ -34,7 +34,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/pom.xml index c2e1a0cc144..0b02e43ab3c 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/pom.xml @@ -46,7 +46,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.cloud diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/SecuritySecureConfig.java b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/SecuritySecureConfig.java index 46bd0144932..5d8da5436b8 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/SecuritySecureConfig.java +++ b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/SecuritySecureConfig.java @@ -19,7 +19,7 @@ import java.util.UUID; import jakarta.servlet.DispatcherType; -import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.security.autoconfigure.SecurityProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/resources/application.yml b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/resources/application.yml index e060d8f6a8f..7dc1ef091d7 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/resources/application.yml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/resources/application.yml @@ -137,4 +137,4 @@ management: endpoint: sbom: application: - location: classpath:bom.json + location: optional:classpath:bom.json diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-war/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-war/pom.xml index b35c6bd3093..ee60b5eab9e 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-war/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-war/pom.xml @@ -41,7 +41,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-zookeeper/pom.xml b/spring-boot-admin-samples/spring-boot-admin-sample-zookeeper/pom.xml index 5eb66e910f5..4234d4f7aa7 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-zookeeper/pom.xml +++ b/spring-boot-admin-samples/spring-boot-admin-sample-zookeeper/pom.xml @@ -33,7 +33,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot diff --git a/spring-boot-admin-server-cloud/pom.xml b/spring-boot-admin-server-cloud/pom.xml index a2beab77024..9aa6b12f1e4 100644 --- a/spring-boot-admin-server-cloud/pom.xml +++ b/spring-boot-admin-server-cloud/pom.xml @@ -50,7 +50,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc @@ -62,7 +62,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc @@ -74,7 +74,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc @@ -105,7 +105,7 @@ test - com.fasterxml.jackson.datatype + tools.jackson.datatype jackson-datatype-json-org test diff --git a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfiguration.java b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfiguration.java index 03bd1911a41..f4c4ba623a0 100644 --- a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfiguration.java +++ b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfiguration.java @@ -18,18 +18,16 @@ import com.netflix.discovery.EurekaClient; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; -import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClient; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import de.codecentric.boot.admin.server.cloud.discovery.DefaultServiceInstanceConverter; @@ -83,7 +81,7 @@ public EurekaServiceInstanceConverter serviceInstanceConverter() { @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean({ ServiceInstanceConverter.class }) - @Conditional(KubernetesDiscoveryClientCondition.class) + @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) public static class KubernetesConverterConfiguration { @Bean @@ -95,22 +93,4 @@ public KubernetesServiceInstanceConverter serviceInstanceConverter( } - private static class KubernetesDiscoveryClientCondition extends AnyNestedCondition { - - KubernetesDiscoveryClientCondition() { - super(ConfigurationPhase.REGISTER_BEAN); - } - - @ConditionalOnBean(KubernetesInformerDiscoveryClient.class) - static class OfficialKubernetesCondition { - - } - - @ConditionalOnBean(KubernetesDiscoveryClient.class) - static class Fabric8KubernetesCondition { - - } - - } - } diff --git a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/package-info.java b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/package-info.java index 08605802853..ecc8fdc08e5 100644 --- a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/package-info.java +++ b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/config/package-info.java @@ -14,9 +14,7 @@ * limitations under the License. */ -@NonNullApi -@NonNullFields +@NullMarked package de.codecentric.boot.admin.server.cloud.config; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/DefaultServiceInstanceConverter.java b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/DefaultServiceInstanceConverter.java index b2c31be6749..2c520903a75 100644 --- a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/DefaultServiceInstanceConverter.java +++ b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/DefaultServiceInstanceConverter.java @@ -20,10 +20,10 @@ import java.util.Map; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.lang.Nullable; import org.springframework.web.util.UriComponentsBuilder; import de.codecentric.boot.admin.server.domain.entities.Instance; diff --git a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/package-info.java b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/package-info.java index a7ecc8d8de5..2355b7c0cad 100644 --- a/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/package-info.java +++ b/spring-boot-admin-server-cloud/src/main/java/de/codecentric/boot/admin/server/cloud/discovery/package-info.java @@ -14,9 +14,7 @@ * limitations under the License. */ -@NonNullApi -@NonNullFields +@NullMarked package de.codecentric.boot.admin.server.cloud.discovery; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/AdminApplicationDiscoveryTest.java b/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/AdminApplicationDiscoveryTest.java index 12f4de1d796..16c9d36616c 100644 --- a/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/AdminApplicationDiscoveryTest.java +++ b/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/AdminApplicationDiscoveryTest.java @@ -18,11 +18,8 @@ import java.net.URI; import java.time.Duration; -import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; import org.json.JSONObject; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,21 +28,24 @@ import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.cloud.client.discovery.simple.InstanceProperties; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; -import org.springframework.http.codec.json.Jackson2JsonDecoder; -import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.http.codec.json.JacksonJsonDecoder; +import org.springframework.http.codec.json.JacksonJsonEncoder; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.ExchangeStrategies; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.datatype.jsonorg.JsonOrgModule; import de.codecentric.boot.admin.server.config.EnableAdminServer; @@ -82,50 +82,51 @@ void lifecycle() { AtomicReference location = new AtomicReference<>(); StepVerifier.create(getEventStream().log()).expectSubscription().then(() -> { - listEmptyInstances(); - location.set(registerInstance()); + StepVerifier.create(listEmptyInstances()).expectNext(true).verifyComplete(); + StepVerifier.create(registerInstance()).consumeNextWith(location::set).verifyComplete(); }) .assertNext((event) -> assertThat(event.opt("type")).isEqualTo("REGISTERED")) .assertNext((event) -> assertThat(event.opt("type")).isEqualTo("STATUS_CHANGED")) .assertNext((event) -> assertThat(event.opt("type")).isEqualTo("ENDPOINTS_DETECTED")) .assertNext((event) -> assertThat(event.opt("type")).isEqualTo("INFO_CHANGED")) .then(() -> { - getInstance(location.get()); - listInstances(); + StepVerifier.create(getInstance(location.get())).expectNext(true).verifyComplete(); + StepVerifier.create(listInstances()).expectNext(true).verifyComplete(); deregisterInstance(); }) .assertNext((event) -> assertThat(event.opt("type")).isEqualTo("DEREGISTERED")) - .then(this::listEmptyInstances) + .then(() -> StepVerifier.create(listEmptyInstances()).expectNext(true).verifyComplete()) .thenCancel() .verify(Duration.ofSeconds(60)); } - private URI registerInstance() { + private Mono registerInstance() { // We register the instance by setting static values for the SimpleDiscoveryClient // and issuing a // InstanceRegisteredEvent that makes sure the instance gets registered. - DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); - serviceInstance.setServiceId("Test-Instance"); - serviceInstance.setUri(URI.create("http://localhost:" + this.port)); - serviceInstance.getMetadata().put("management.context-path", "/mgmt"); - this.simpleDiscovery.getInstances().put("Test-Application", singletonList(serviceInstance)); + InstanceProperties instanceProps = new InstanceProperties(); + instanceProps.setServiceId("Test-Instance"); + instanceProps.setUri(URI.create("http://localhost:" + this.port)); + instanceProps.getMetadata().put("management.context-path", "/mgmt"); + this.simpleDiscovery.getInstances().put("Test-Instance", singletonList(instanceProps)); this.instance.publishEvent(new InstanceRegisteredEvent<>(new Object(), null)); // To get the location of the registered instances we fetch the instance with the // name. - List applications = this.webClient.get() + //@formatter:off + return this.webClient.get() .uri("/instances?name=Test-Instance") .accept(MediaType.APPLICATION_JSON) .exchange() - .expectStatus() - .isOk() - .returnResult(JSONObject.class) - .getResponseBody() + .returnResult(JSONObject.class).getResponseBody() .collectList() - .block(); - assertThat(applications).hasSize(1); - return URI.create("http://localhost:" + this.port + "/instances/" + applications.get(0).optString("id")); + .map((applications) -> { + assertThat(applications).hasSize(1); + return URI + .create("http://localhost:" + this.port + "/instances/" + applications.get(0).optString("id")); + }); + //@formatter:on } private void deregisterInstance() { @@ -143,46 +144,55 @@ private Flux getEventStream() { //@formatter:on } - private void getInstance(URI uri) { + private Mono getInstance(URI uri) { //@formatter:off - this.webClient.get().uri(uri).accept(MediaType.APPLICATION_JSON) + return this.webClient.get().uri(uri).accept(MediaType.APPLICATION_JSON) .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$.registration.name").isEqualTo("Test-Instance") - .jsonPath("$.statusInfo.status").isEqualTo("UP") - .jsonPath("$.info.test").isEqualTo("foobar"); + .returnResult(String.class).getResponseBody().single() + .map((body) -> { + assertThat(body).contains("\"name\":\"Test-Instance\""); + assertThat(body).contains("\"status\":\"UP\""); + assertThat(body).contains("\"test\":\"foobar\""); + return true; + }); //@formatter:on } - private void listInstances() { + private Mono listInstances() { //@formatter:off - this.webClient.get().uri("/instances").accept(MediaType.APPLICATION_JSON) + return this.webClient.get().uri("/instances").accept(MediaType.APPLICATION_JSON) .exchange() - .expectStatus().isOk() - .expectBody() - .jsonPath("$[0].registration.name").isEqualTo("Test-Instance") - .jsonPath("$[0].statusInfo.status").isEqualTo("UP") - .jsonPath("$[0].info.test").isEqualTo("foobar"); + .returnResult(String.class).getResponseBody().single() + .map((body) -> { + assertThat(body).contains("\"name\":\"Test-Instance\""); + assertThat(body).contains("\"status\":\"UP\""); + assertThat(body).contains("\"test\":\"foobar\""); + return true; + }); //@formatter:on } - private void listEmptyInstances() { + private Mono listEmptyInstances() { //@formatter:off - this.webClient.get().uri("/instances").accept(MediaType.APPLICATION_JSON) + return this.webClient.get().uri("/instances").accept(MediaType.APPLICATION_JSON) .exchange() - .expectStatus().isOk() - .expectBody().json("[]"); + .returnResult(String.class).getResponseBody() + .collectList() + .map((list) -> { + assertThat(list).hasSize(1); + assertThat(list.get(0)).isEqualTo("[]"); + return true; + }); //@formatter:on } private WebTestClient createWebClient(int port) { - ObjectMapper mapper = new ObjectMapper().registerModule(new JsonOrgModule()); + JsonMapper mapper = JsonMapper.builder().addModule(new JsonOrgModule()).build(); return WebTestClient.bindToServer() .baseUrl("http://localhost:" + port) .exchangeStrategies(ExchangeStrategies.builder().codecs((configurer) -> { - configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper)); - configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper)); + configurer.defaultCodecs().jacksonJsonDecoder(new JacksonJsonDecoder(mapper)); + configurer.defaultCodecs().jacksonJsonEncoder(new JacksonJsonEncoder(mapper)); }).build()) .build(); } diff --git a/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfigurationTest.java b/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfigurationTest.java index 34ab4fb8001..33c91522122 100644 --- a/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfigurationTest.java +++ b/spring-boot-admin-server-cloud/src/test/java/de/codecentric/boot/admin/server/cloud/config/AdminServerDiscoveryAutoConfigurationTest.java @@ -17,20 +17,17 @@ package de.codecentric.boot.admin.server.cloud.config; import com.netflix.discovery.EurekaClient; -import io.kubernetes.client.openapi.apis.CoreV1Api; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.http.client.reactive.ClientHttpConnectorAutoConfiguration; -import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.http.client.autoconfigure.reactive.ReactiveHttpClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.webclient.autoconfigure.WebClientAutoConfiguration; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; import org.springframework.cloud.commons.util.UtilAutoConfiguration; -import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; -import org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesDiscoveryClient; import de.codecentric.boot.admin.server.cloud.discovery.DefaultServiceInstanceConverter; import de.codecentric.boot.admin.server.cloud.discovery.EurekaServiceInstanceConverter; @@ -46,9 +43,9 @@ class AdminServerDiscoveryAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class, - ClientHttpConnectorAutoConfiguration.class, WebClientAutoConfiguration.class, - AdminServerAutoConfiguration.class, AdminServerDiscoveryAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class, ReactiveHttpClientAutoConfiguration.class, + WebClientAutoConfiguration.class, AdminServerAutoConfiguration.class, + AdminServerDiscoveryAutoConfiguration.class)) .withUserConfiguration(AdminServerMarkerConfiguration.class); @Test @@ -62,24 +59,16 @@ void defaultServiceInstanceConverter() { void eurekaServiceInstanceConverter() { this.contextRunner.withBean(EurekaClient.class, () -> mock(EurekaClient.class)) .withBean(DiscoveryClient.class, () -> mock(DiscoveryClient.class)) - .run((context) -> assertThat(context).getBean(ServiceInstanceConverter.class) + .run((context) -> assertThat(context.getBean(ServiceInstanceConverter.class)) .isInstanceOf(EurekaServiceInstanceConverter.class)); } @Test - void officialKubernetesServiceInstanceConverter() { + void kubernetesServiceInstanceConverter() { this.contextRunner.withUserConfiguration(KubernetesDiscoveryPropertiesConfiguration.class) - .withBean(CoreV1Api.class, () -> mock(CoreV1Api.class)) - .withBean(KubernetesInformerDiscoveryClient.class, () -> mock(KubernetesInformerDiscoveryClient.class)) - .run((context) -> assertThat(context).getBean(ServiceInstanceConverter.class) - .isInstanceOf(KubernetesServiceInstanceConverter.class)); - } - - @Test - void fabric8KubernetesServiceInstanceConverter() { - this.contextRunner.withUserConfiguration(KubernetesDiscoveryPropertiesConfiguration.class) - .withBean(KubernetesDiscoveryClient.class, () -> mock(KubernetesDiscoveryClient.class)) - .run((context) -> assertThat(context).getBean(ServiceInstanceConverter.class) + .withBean(DiscoveryClient.class, () -> mock(DiscoveryClient.class)) + .withPropertyValues("spring.main.cloud-platform=KUBERNETES") + .run((context) -> assertThat(context.getBean(ServiceInstanceConverter.class)) .isInstanceOf(KubernetesServiceInstanceConverter.class)); } @@ -87,7 +76,7 @@ void fabric8KubernetesServiceInstanceConverter() { void customServiceInstanceConverter() { this.contextRunner.withUserConfiguration(SimpleDiscoveryClientAutoConfiguration.class) .withBean(CustomServiceInstanceConverter.class) - .run((context) -> assertThat(context).getBean(ServiceInstanceConverter.class) + .run((context) -> assertThat(context.getBean(ServiceInstanceConverter.class)) .isInstanceOf(CustomServiceInstanceConverter.class)); } diff --git a/spring-boot-admin-server-ui/pom.xml b/spring-boot-admin-server-ui/pom.xml index 265c02476fd..f17a4aa6f96 100644 --- a/spring-boot-admin-server-ui/pom.xml +++ b/spring-boot-admin-server-ui/pom.xml @@ -38,7 +38,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc true diff --git a/spring-boot-admin-server-ui/src/main/frontend/components.d.ts b/spring-boot-admin-server-ui/src/main/frontend/components.d.ts index 604df6ad55e..f79ecaefebb 100644 --- a/spring-boot-admin-server-ui/src/main/frontend/components.d.ts +++ b/spring-boot-admin-server-ui/src/main/frontend/components.d.ts @@ -3,7 +3,7 @@ // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 // biome-ignore lint: disable -export {} +export {}; /* prettier-ignore */ declare module 'vue' { diff --git a/spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.stories.ts b/spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.stories.ts new file mode 100644 index 00000000000..4e7f8b76a28 --- /dev/null +++ b/spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.stories.ts @@ -0,0 +1,239 @@ +/* + * Copyright 2014-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ref } from 'vue'; + +import SbaAccordion from './sba-accordion.vue'; + +export default { + component: SbaAccordion, + title: 'Components/Accordion', +}; + +const Template = (args) => { + return { + components: { SbaAccordion }, + setup() { + const isOpen = ref(args.modelValue ?? true); + return { args, isOpen }; + }, + template: ` + + + + + +
+ Current state: {{ isOpen ? 'Open' : 'Closed' }} +
+ `, + }; +}; + +export const DefaultOpen = { + render: Template, + + args: { + modelValue: true, + title: 'Application Information', + slot: `
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae dolor ac ante ornare pharetra. + Proin laoreet ex et lacinia hendrerit. Fusce sed justo at nulla pellentesque maximus sed at diam.

+

Suspendisse sem lorem, lobortis vel orci quis, efficitur porta massa. In vel neque justo. + Maecenas dapibus quam ut nisl porta, molestie egestas felis maximus.

+
`, + }, +}; + +export const DefaultClosed = { + render: Template, + + args: { + modelValue: false, + title: 'System Properties', + slot: `
+

This content is initially hidden. Click the title to expand the accordion.

+
`, + }, +}; + +export const WithKeyValueTable = { + render: Template, + + args: { + modelValue: true, + title: 'Health Details', + slot: ` +
+
+
Status
+
UP
+
+
+
Disk Space
+
10.5 GB free
+
+
+
Database
+
Connected
+
+
+
Memory
+
512 MB / 2 GB
+
+
`, + }, +}; + +export const WithActions = { + render: Template, + + args: { + modelValue: true, + title: 'Configuration Settings', + actions: + '', + slot: `
+

Configuration content with action buttons in the header.

+
`, + }, +}; + +export const WithId = { + render: (args) => { + return { + components: { SbaAccordion }, + setup() { + const isOpen = ref(args.modelValue ?? true); + return { args, isOpen }; + }, + template: ` +
+

+ This accordion has an ID and will persist its state in localStorage. + Try toggling it and refreshing the page. +

+ + + + +
+ Current state: {{ isOpen ? 'Open' : 'Closed' }} +
+ LocalStorage key: de.codecentric.spring-boot-admin.accordion.${args.id}.open +
+
+ `, + }; + }, + + args: { + id: 'storybook-example', + modelValue: true, + title: 'Persisted State Example', + slot: `
+

This accordion's open/closed state is stored in localStorage using the ID "storybook-example".

+

Try toggling it and refreshing the browser to see the state persist.

+
`, + }, +}; + +export const MultipleAccordions = { + render: (args) => { + return { + components: { SbaAccordion }, + setup() { + const accordion1Open = ref(true); + const accordion2Open = ref(false); + const accordion3Open = ref(true); + return { args, accordion1Open, accordion2Open, accordion3Open }; + }, + template: ` +
+ + + + + + + + + + + + + + + + +
+ States: + 1: {{ accordion1Open ? 'Open' : 'Closed' }} | + 2: {{ accordion2Open ? 'Open' : 'Closed' }} | + 3: {{ accordion3Open ? 'Open' : 'Closed' }} +
+
+ `, + }; + }, + + args: {}, +}; + +export const NestedContent = { + render: Template, + + args: { + modelValue: true, + title: 'Advanced Configuration', + slot: ` +
+
+

Server Settings

+
    +
  • Port: 8080
  • +
  • Context Path: /admin
  • +
  • SSL Enabled: false
  • +
+
+
+

Monitoring

+
    +
  • Interval: 10000ms
  • +
  • Timeout: 5000ms
  • +
  • Retries: 3
  • +
+
+
`, + }, +}; diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/sba-accordion.vue b/spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.vue similarity index 84% rename from spring-boot-admin-server-ui/src/main/frontend/views/instances/details/sba-accordion.vue rename to spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.vue index 28c06c41c0c..905692e9b23 100644 --- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/details/sba-accordion.vue +++ b/spring-boot-admin-server-ui/src/main/frontend/components/sba-accordion.vue @@ -18,10 +18,14 @@