Support isolated actuator ObjectMapper
Closes gh-20291
This commit is contained in:
@@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
|
||||
@@ -81,7 +82,7 @@ public class ConditionsReportEndpoint {
|
||||
/**
|
||||
* A description of an application's condition evaluation.
|
||||
*/
|
||||
public static final class ConditionsDescriptor {
|
||||
public static final class ConditionsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextConditionsDescriptor> contexts;
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Endpoint Jackson support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(JacksonAutoConfiguration.class)
|
||||
public class JacksonEndpointAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "management.endpoints.jackson.isolated-object-mapper", matchIfMissing = true)
|
||||
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
|
||||
public EndpointObjectMapper endpointObjectMapper() {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
|
||||
return () -> objectMapper;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Actuator Jackson auto-configuration.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.jackson;
|
||||
@@ -23,6 +23,10 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Priority;
|
||||
import jakarta.ws.rs.Priorities;
|
||||
import jakarta.ws.rs.ext.ContextResolver;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.server.model.Resource;
|
||||
|
||||
@@ -35,7 +39,9 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnMa
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
@@ -53,6 +59,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -98,6 +105,13 @@ class JerseyWebEndpointManagementContextConfiguration {
|
||||
return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(health, healthEndpointGroups);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EndpointObjectMapper.class)
|
||||
ResourceConfigCustomizer endpointObjectMapperResourceConfigCustomizer(EndpointObjectMapper endpointObjectMapper) {
|
||||
return (config) -> config.register(new EndpointObjectMapperContextResolver(endpointObjectMapper),
|
||||
ContextResolver.class);
|
||||
}
|
||||
|
||||
private boolean shouldRegisterLinksMapping(WebEndpointProperties properties, Environment environment,
|
||||
String basePath) {
|
||||
return properties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
|
||||
@@ -192,4 +206,24 @@ class JerseyWebEndpointManagementContextConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ContextResolver} used to obtain the {@link ObjectMapper} that should be used
|
||||
* for {@link OperationResponseBody} instances.
|
||||
*/
|
||||
@Priority(Priorities.USER - 100)
|
||||
private static final class EndpointObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
|
||||
|
||||
private final EndpointObjectMapper endpointObjectMapper;
|
||||
|
||||
private EndpointObjectMapperContextResolver(EndpointObjectMapper endpointObjectMapper) {
|
||||
this.endpointObjectMapper = endpointObjectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMapper getContext(Class<?> type) {
|
||||
return OperationResponseBody.class.isAssignableFrom(type) ? this.endpointObjectMapper.get() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -17,9 +17,16 @@
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
|
||||
@@ -28,7 +35,9 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfi
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
@@ -48,7 +57,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
@@ -114,4 +130,55 @@ public class WebFluxEndpointManagementContextConfiguration {
|
||||
corsProperties.toCorsConfiguration());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EndpointObjectMapper.class)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor serverCodecConfigurerEndpointObjectMapperBeanPostProcessor(
|
||||
EndpointObjectMapper endpointObjectMapper) {
|
||||
return new ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(endpointObjectMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to apply {@link EndpointObjectMapper} for
|
||||
* {@link OperationResponseBody} to {@link Jackson2JsonEncoder} instances.
|
||||
*/
|
||||
private static class ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private static final List<MediaType> MEDIA_TYPES = Collections
|
||||
.unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json")));
|
||||
|
||||
private final EndpointObjectMapper endpointObjectMapper;
|
||||
|
||||
ServerCodecConfigurerEndpointObjectMapperBeanPostProcessor(EndpointObjectMapper endpointObjectMapper) {
|
||||
this.endpointObjectMapper = endpointObjectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ServerCodecConfigurer) {
|
||||
process((ServerCodecConfigurer) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void process(ServerCodecConfigurer configurer) {
|
||||
for (HttpMessageWriter<?> writer : configurer.getWriters()) {
|
||||
if (writer instanceof EncoderHttpMessageWriter) {
|
||||
process(((EncoderHttpMessageWriter<?>) writer).getEncoder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void process(Encoder<?> encoder) {
|
||||
if (encoder instanceof Jackson2JsonEncoder) {
|
||||
Jackson2JsonEncoder jackson2JsonEncoder = (Jackson2JsonEncoder) encoder;
|
||||
jackson2JsonEncoder.registerObjectMappersForType(OperationResponseBody.class, (associations) -> {
|
||||
ObjectMapper objectMapper = this.endpointObjectMapper.get();
|
||||
MEDIA_TYPES.forEach((mimeType) -> associations.put(mimeType, objectMapper));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
|
||||
@@ -28,7 +33,9 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfi
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
@@ -49,9 +56,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Spring MVC
|
||||
@@ -116,4 +128,46 @@ public class WebMvcEndpointManagementContextConfiguration {
|
||||
corsProperties.toCorsConfiguration());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EndpointObjectMapper.class)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static EndpointObjectMapperWebMvcConfigurer endpointObjectMapperWebMvcConfigurer(
|
||||
EndpointObjectMapper endpointObjectMapper) {
|
||||
return new EndpointObjectMapperWebMvcConfigurer(endpointObjectMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WebMvcConfigurer} to apply {@link EndpointObjectMapper} for
|
||||
* {@link OperationResponseBody} to {@link MappingJackson2HttpMessageConverter}
|
||||
* instances.
|
||||
*/
|
||||
static class EndpointObjectMapperWebMvcConfigurer implements WebMvcConfigurer {
|
||||
|
||||
private static final List<MediaType> MEDIA_TYPES = Collections
|
||||
.unmodifiableList(Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json")));
|
||||
|
||||
private final EndpointObjectMapper endpointObjectMapper;
|
||||
|
||||
EndpointObjectMapperWebMvcConfigurer(EndpointObjectMapper endpointObjectMapper) {
|
||||
this.endpointObjectMapper = endpointObjectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
for (HttpMessageConverter<?> converter : converters) {
|
||||
if (converter instanceof MappingJackson2HttpMessageConverter) {
|
||||
configure((MappingJackson2HttpMessageConverter) converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configure(MappingJackson2HttpMessageConverter converter) {
|
||||
converter.registerObjectMappersForType(OperationResponseBody.class, (associations) -> {
|
||||
ObjectMapper objectMapper = this.endpointObjectMapper.get();
|
||||
MEDIA_TYPES.forEach((mimeType) -> associations.put(mimeType, objectMapper));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to enable or disable all endpoints by default."
|
||||
},
|
||||
{
|
||||
"name": "management.endpoints.jackson.isolated-object-mapper",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to use an isolated object mapper to serialize endpoint JSON.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "management.endpoints.jmx.domain",
|
||||
"defaultValue": "org.springframework.boot"
|
||||
|
||||
@@ -17,6 +17,7 @@ org.springframework.boot.actuate.autoconfigure.couchbase.CouchbaseReactiveHealth
|
||||
org.springframework.boot.actuate.autoconfigure.data.elasticsearch.ElasticsearchReactiveHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticsearchRestHealthContributorAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration
|
||||
org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonEndpointAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JacksonEndpointAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(JacksonEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperWhenNoProperty() {
|
||||
this.runner.run((context) -> assertThat(context).hasSingleBean(EndpointObjectMapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperWhenPropertyTrue() {
|
||||
this.runner.withPropertyValues("management.endpoints.jackson.isolated-object-mapper=true")
|
||||
.run((context) -> assertThat(context).hasSingleBean(EndpointObjectMapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperWhenPropertyFalse() {
|
||||
this.runner.withPropertyValues("management.endpoints.jackson.isolated-object-mapper=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(EndpointObjectMapper.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class TestEndpointMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
TestEndpointObjectMapper testEndpointObjectMapper() {
|
||||
return new TestEndpointObjectMapper();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestEndpointObjectMapper implements EndpointObjectMapper {
|
||||
|
||||
@Override
|
||||
public ObjectMapper get() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.integrationtest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} that creates an {@link EndpointObjectMapper} that
|
||||
* reverses all strings.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration
|
||||
class EndpointObjectMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
EndpointObjectMapper endpointObjectMapper() {
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(String.class, new ReverseStringSerializer());
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(module).build();
|
||||
return () -> objectMapper;
|
||||
}
|
||||
|
||||
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
|
||||
|
||||
ReverseStringSerializer() {
|
||||
super(String.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty(SerializerProvider prov, Object value) {
|
||||
return ((String) value).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
serialize(value, gen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
|
||||
TypeSerializer typeSer) throws IOException {
|
||||
serialize(value, gen);
|
||||
}
|
||||
|
||||
private void serialize(Object value, JsonGenerator gen) throws IOException {
|
||||
StringBuilder builder = new StringBuilder((String) value);
|
||||
gen.writeString(builder.reverse().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.integrationtest;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -44,6 +45,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for the Jersey actuator endpoints.
|
||||
*
|
||||
@@ -87,7 +90,22 @@ class JerseyEndpointIntegrationTests {
|
||||
.responseTimeout(Duration.ofMinutes(5)).build();
|
||||
client.get().uri("/actuator").exchange().expectStatus().isUnauthorized();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperCanBeApplied() {
|
||||
WebApplicationContextRunner contextRunner = getContextRunner(new Class<?>[] { EndpointsConfiguration.class,
|
||||
ResourceConfigConfiguration.class, EndpointObjectMapperConfiguration.class });
|
||||
contextRunner.run((context) -> {
|
||||
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
|
||||
.getWebServer().getPort();
|
||||
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port)
|
||||
.responseTimeout(Duration.ofMinutes(5)).build();
|
||||
client.get().uri("/actuator/beans").exchange().expectStatus().isOk().expectBody().consumeWith((result) -> {
|
||||
String json = new String(result.getResponseBody(), StandardCharsets.UTF_8);
|
||||
assertThat(json).contains("\"scope\":\"notelgnis\"");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected void testJerseyEndpoints(Class<?>[] userConfigurations) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.integrationtest;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
|
||||
@@ -36,6 +38,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for the WebFlux actuator endpoints.
|
||||
*
|
||||
@@ -68,6 +72,19 @@ class WebFluxEndpointIntegrationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperCanBeApplied() {
|
||||
this.contextRunner.withUserConfiguration(EndpointObjectMapperConfiguration.class)
|
||||
.withPropertyValues("management.endpoints.web.exposure.include:*").run((context) -> {
|
||||
WebTestClient client = createWebTestClient(context);
|
||||
client.get().uri("/actuator/beans").exchange().expectStatus().isOk().expectBody()
|
||||
.consumeWith((result) -> {
|
||||
String json = new String(result.getResponseBody(), StandardCharsets.UTF_8);
|
||||
assertThat(json).contains("\"scope\":\"notelgnis\"");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private WebTestClient createWebTestClient(ApplicationContext context) {
|
||||
return WebTestClient.bindToApplicationContext(context).configureClient().baseUrl("https://spring.example.org")
|
||||
.build();
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.test.context.TestSecurityContextHolder;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
|
||||
@@ -140,6 +141,16 @@ class WebMvcEndpointIntegrationTests {
|
||||
mockMvc.perform(get("/actuator").accept("*/*")).andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
void endpointObjectMapperCanBeApplied() throws Exception {
|
||||
this.context = new AnnotationConfigServletWebApplicationContext();
|
||||
this.context.register(EndpointObjectMapperConfiguration.class, DefaultConfiguration.class);
|
||||
TestPropertyValues.of("management.endpoints.web.exposure.include=*").applyTo(this.context);
|
||||
MockMvc mockMvc = doCreateMockMvc();
|
||||
MvcResult result = mockMvc.perform(get("/actuator/beans")).andExpect(status().isOk()).andReturn();
|
||||
assertThat(result.getResponse().getContentAsString()).contains("\"scope\":\"notelgnis\"");
|
||||
}
|
||||
|
||||
private MockMvc createSecureMockMvc() {
|
||||
return doCreateMockMvc(springSecurity());
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -55,7 +56,7 @@ public class AuditEventsEndpoint {
|
||||
/**
|
||||
* Description of an application's {@link AuditEvent audit events}.
|
||||
*/
|
||||
public static final class AuditEventsDescriptor {
|
||||
public static final class AuditEventsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<AuditEvent> events;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -73,7 +74,7 @@ public class BeansEndpoint {
|
||||
/**
|
||||
* Description of an application's beans.
|
||||
*/
|
||||
public static final class BeansDescriptor {
|
||||
public static final class BeansDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextBeansDescriptor> contexts;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
@@ -148,7 +149,7 @@ public class CachesEndpoint {
|
||||
/**
|
||||
* Description of the caches.
|
||||
*/
|
||||
public static final class CachesDescriptor {
|
||||
public static final class CachesDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, CacheManagerDescriptor> cacheManagers;
|
||||
|
||||
@@ -182,7 +183,7 @@ public class CachesEndpoint {
|
||||
/**
|
||||
* Description of a {@link Cache}.
|
||||
*/
|
||||
public static class CacheDescriptor {
|
||||
public static class CacheDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String target;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.actuate.context;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -71,7 +72,7 @@ public class ShutdownEndpoint implements ApplicationContextAware {
|
||||
/**
|
||||
* Description of the shutdown.
|
||||
*/
|
||||
public static class ShutdownDescriptor {
|
||||
public static class ShutdownDescriptor implements OperationResponseBody {
|
||||
|
||||
private static final ShutdownDescriptor DEFAULT = new ShutdownDescriptor("Shutting down, bye...");
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizableData;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizingFunction;
|
||||
@@ -564,7 +565,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
||||
* Description of an application's
|
||||
* {@link ConfigurationProperties @ConfigurationProperties} beans.
|
||||
*/
|
||||
public static final class ConfigurationPropertiesDescriptor {
|
||||
public static final class ConfigurationPropertiesDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextConfigurationPropertiesDescriptor> contexts;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -32,7 +32,9 @@ public interface Operation {
|
||||
OperationType getType();
|
||||
|
||||
/**
|
||||
* Invoke the underlying operation using the given {@code context}.
|
||||
* Invoke the underlying operation using the given {@code context}. Results intended
|
||||
* to be returned in the body of the response should additionally implement
|
||||
* {@link OperationResponseBody}.
|
||||
* @param context the context in to use when invoking the operation
|
||||
* @return the result of the operation, may be {@code null}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Tagging interface used to indicate that an operation result is intended to be returned
|
||||
* in the body of the response. Primarily intended to support JSON serialzation using an
|
||||
* endpoint specific {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public interface OperationResponseBody {
|
||||
|
||||
/**
|
||||
* Return a {@link OperationResponseBody} {@link Map} instance containing entires from
|
||||
* the given {@code map}.
|
||||
* @param <K> the key type
|
||||
* @param <V> the value type
|
||||
* @param map the source map or {@code null}
|
||||
* @return a {@link OperationResponseBody} version of the map or {@code null}
|
||||
*/
|
||||
static <K, V> Map<K, V> of(Map<K, V> map) {
|
||||
return (map != null) ? new OperationResponseBodyMap<>(map) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link LinkedHashMap} to support {@link OperationResponseBody#of(java.util.Map)}.
|
||||
*
|
||||
* @param <K> the key type
|
||||
* @param <V> the value type
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OperationResponseBodyMap<K, V> extends LinkedHashMap<K, V> implements OperationResponseBody {
|
||||
|
||||
OperationResponseBodyMap(Map<? extends K, ? extends V> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
|
||||
/**
|
||||
* Interface used to supply the {@link ObjectMapper} that should be used when serializing
|
||||
* {@link OperationResponseBody} endpoint results.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.0.0
|
||||
* @see OperationResponseBody
|
||||
*/
|
||||
public interface EndpointObjectMapper {
|
||||
|
||||
/**
|
||||
* Return the {@link ObjectMapper} that should be used to serialize
|
||||
* {@link OperationResponseBody} endpoint results.
|
||||
* @return the object mapper
|
||||
*/
|
||||
ObjectMapper get();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson support classes for actuator endpoints.
|
||||
*/
|
||||
package org.springframework.boot.actuate.endpoint.jackson;
|
||||
@@ -43,6 +43,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
|
||||
import org.springframework.boot.actuate.endpoint.InvocationContext;
|
||||
import org.springframework.boot.actuate.endpoint.OperationArgumentResolver;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.ProducibleOperationArgumentResolver;
|
||||
import org.springframework.boot.actuate.endpoint.SecurityContext;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
@@ -323,7 +324,8 @@ public class JerseyEndpointResourceFactory {
|
||||
public Response apply(ContainerRequestContext request) {
|
||||
Map<String, Link> links = this.linksResolver
|
||||
.resolveLinks(request.getUriInfo().getAbsolutePath().toString());
|
||||
return Response.ok(Collections.singletonMap("_links", links)).build();
|
||||
Map<String, Map<String, Link>> entity = OperationResponseBody.of(Collections.singletonMap("_links", links));
|
||||
return Response.ok(entity).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
@@ -87,8 +88,8 @@ public class WebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointHandle
|
||||
public Map<String, Map<String, Link>> links(ServerWebExchange exchange) {
|
||||
String requestUri = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).replaceQuery(null)
|
||||
.toUriString();
|
||||
return Collections.singletonMap("_links",
|
||||
WebFluxEndpointHandlerMapping.this.linksResolver.resolveLinks(requestUri));
|
||||
Map<String, Link> links = WebFluxEndpointHandlerMapping.this.linksResolver.resolveLinks(requestUri);
|
||||
return OperationResponseBody.of(Collections.singletonMap("_links", links));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
@@ -84,8 +85,9 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
|
||||
@ResponseBody
|
||||
@Reflective
|
||||
public Map<String, Map<String, Link>> links(HttpServletRequest request, HttpServletResponse response) {
|
||||
return Collections.singletonMap("_links",
|
||||
WebMvcEndpointHandlerMapping.this.linksResolver.resolveLinks(request.getRequestURL().toString()));
|
||||
Map<String, Link> links = WebMvcEndpointHandlerMapping.this.linksResolver
|
||||
.resolveLinks(request.getRequestURL().toString());
|
||||
return OperationResponseBody.of(Collections.singletonMap("_links", links));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizableData;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizingFunction;
|
||||
@@ -203,7 +204,7 @@ public class EnvironmentEndpoint {
|
||||
/**
|
||||
* Description of an {@link Environment}.
|
||||
*/
|
||||
public static final class EnvironmentDescriptor {
|
||||
public static final class EnvironmentDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<String> activeProfiles;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.MigrationInfo;
|
||||
import org.flywaydb.core.api.MigrationState;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -69,7 +70,7 @@ public class FlywayEndpoint {
|
||||
/**
|
||||
* Description of an application's {@link Flyway} beans.
|
||||
*/
|
||||
public static final class FlywayBeansDescriptor {
|
||||
public static final class FlywayBeansDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextFlywayBeansDescriptor> contexts;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.actuate.health;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
|
||||
/**
|
||||
* A component that contributes data to results returned from the {@link HealthEndpoint}.
|
||||
*
|
||||
@@ -26,7 +28,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
* @see Health
|
||||
* @see CompositeHealth
|
||||
*/
|
||||
public abstract class HealthComponent {
|
||||
public abstract class HealthComponent implements OperationResponseBody {
|
||||
|
||||
HealthComponent() {
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.info;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -51,7 +52,7 @@ public class InfoEndpoint {
|
||||
for (InfoContributor contributor : this.infoContributors) {
|
||||
contributor.contribute(builder);
|
||||
}
|
||||
return builder.build().getDetails();
|
||||
return OperationResponseBody.of(builder.build().getDetails());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
@@ -99,7 +100,7 @@ public class IntegrationGraphEndpoint {
|
||||
/**
|
||||
* Description of a {@link Graph}.
|
||||
*/
|
||||
public static class GraphDescriptor {
|
||||
public static class GraphDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, Object> contentDescriptor;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import liquibase.database.DatabaseFactory;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -106,7 +107,7 @@ public class LiquibaseEndpoint {
|
||||
/**
|
||||
* Description of an application's {@link SpringLiquibase} beans.
|
||||
*/
|
||||
public static final class LiquibaseBeansDescriptor {
|
||||
public static final class LiquibaseBeansDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextLiquibaseBeansDescriptor> contexts;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
@@ -121,7 +122,7 @@ public class LoggersEndpoint {
|
||||
/**
|
||||
* Description of loggers.
|
||||
*/
|
||||
public static class LoggersDescriptor {
|
||||
public static class LoggersDescriptor implements OperationResponseBody {
|
||||
|
||||
/**
|
||||
* Empty description.
|
||||
@@ -158,7 +159,7 @@ public class LoggersEndpoint {
|
||||
/**
|
||||
* Description of levels configured for a given logger.
|
||||
*/
|
||||
public static class LoggerLevelsDescriptor {
|
||||
public static class LoggerLevelsDescriptor implements OperationResponseBody {
|
||||
|
||||
private String configuredLevel;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class ThreadDumpEndpoint {
|
||||
/**
|
||||
* Description of a thread dump.
|
||||
*/
|
||||
public static final class ThreadDumpDescriptor {
|
||||
public static final class ThreadDumpDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<ThreadInfo> threads;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
@@ -159,7 +160,7 @@ public class MetricsEndpoint {
|
||||
/**
|
||||
* Description of metric names.
|
||||
*/
|
||||
public static final class MetricNamesDescriptor {
|
||||
public static final class MetricNamesDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Set<String> names;
|
||||
|
||||
@@ -176,7 +177,7 @@ public class MetricsEndpoint {
|
||||
/**
|
||||
* Description of a metric.
|
||||
*/
|
||||
public static final class MetricDescriptor {
|
||||
public static final class MetricDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.quartz.Trigger.TriggerState;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.quartz.impl.matchers.GroupMatcher;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizableData;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizingFunction;
|
||||
@@ -236,8 +237,9 @@ public class QuartzEndpoint {
|
||||
return null;
|
||||
}
|
||||
TriggerState triggerState = this.scheduler.getTriggerState(triggerKey);
|
||||
return TriggerDescriptor.of(trigger).buildDetails(triggerState,
|
||||
sanitizeJobDataMap(trigger.getJobDataMap(), showUnsanitized));
|
||||
TriggerDescriptor triggerDescriptor = TriggerDescriptor.of(trigger);
|
||||
Map<String, Object> jobDataMap = sanitizeJobDataMap(trigger.getJobDataMap(), showUnsanitized);
|
||||
return OperationResponseBody.of(triggerDescriptor.buildDetails(triggerState, jobDataMap));
|
||||
}
|
||||
|
||||
private static Duration getIntervalDuration(long amount, IntervalUnit unit) {
|
||||
@@ -279,7 +281,7 @@ public class QuartzEndpoint {
|
||||
/**
|
||||
* Description of available job and trigger group names.
|
||||
*/
|
||||
public static final class QuartzDescriptor {
|
||||
public static final class QuartzDescriptor implements OperationResponseBody {
|
||||
|
||||
private final GroupNamesDescriptor jobs;
|
||||
|
||||
@@ -320,7 +322,7 @@ public class QuartzEndpoint {
|
||||
/**
|
||||
* Description of each group identified by name.
|
||||
*/
|
||||
public static class QuartzGroupsDescriptor {
|
||||
public static class QuartzGroupsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, Object> groups;
|
||||
|
||||
@@ -337,7 +339,7 @@ public class QuartzEndpoint {
|
||||
/**
|
||||
* Description of the {@link JobDetail jobs} in a given group.
|
||||
*/
|
||||
public static final class QuartzJobGroupSummaryDescriptor {
|
||||
public static final class QuartzJobGroupSummaryDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String group;
|
||||
|
||||
@@ -382,7 +384,7 @@ public class QuartzEndpoint {
|
||||
/**
|
||||
* Description of a {@link Job Quartz Job}.
|
||||
*/
|
||||
public static final class QuartzJobDetailsDescriptor {
|
||||
public static final class QuartzJobDetailsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String group;
|
||||
|
||||
@@ -449,7 +451,7 @@ public class QuartzEndpoint {
|
||||
/**
|
||||
* Description of the {@link Trigger triggers} in a given group.
|
||||
*/
|
||||
public static final class QuartzTriggerGroupSummaryDescriptor {
|
||||
public static final class QuartzTriggerGroupSummaryDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String group;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
|
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.ScheduledTasksEndpointRuntimeHints;
|
||||
@@ -75,7 +76,7 @@ public class ScheduledTasksEndpoint {
|
||||
/**
|
||||
* Description of an application's scheduled {@link Task Tasks}.
|
||||
*/
|
||||
public static final class ScheduledTasksDescriptor {
|
||||
public static final class ScheduledTasksDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<TaskDescriptor> cron;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
@@ -70,7 +71,7 @@ public class SessionsEndpoint {
|
||||
/**
|
||||
* Description of user's {@link Session sessions}.
|
||||
*/
|
||||
public static final class SessionsDescriptor {
|
||||
public static final class SessionsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<SessionDescriptor> sessions;
|
||||
|
||||
@@ -87,7 +88,7 @@ public class SessionsEndpoint {
|
||||
/**
|
||||
* Description of user's {@link Session session}.
|
||||
*/
|
||||
public static final class SessionDescriptor {
|
||||
public static final class SessionDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String id;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.boot.SpringBootVersion;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
@@ -68,7 +69,7 @@ public class StartupEndpoint {
|
||||
/**
|
||||
* Description of an application startup.
|
||||
*/
|
||||
public static final class StartupDescriptor {
|
||||
public static final class StartupDescriptor implements OperationResponseBody {
|
||||
|
||||
private final String springBootVersion;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.web.exchanges;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -51,7 +52,7 @@ public class HttpExchangesEndpoint {
|
||||
/**
|
||||
* Description of an application's {@link HttpExchange} entries.
|
||||
*/
|
||||
public static final class HttpExchangesDescriptor {
|
||||
public static final class HttpExchangesDescriptor implements OperationResponseBody {
|
||||
|
||||
private final List<HttpExchange> exchanges;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -64,7 +65,7 @@ public class MappingsEndpoint {
|
||||
/**
|
||||
* Description of an application's request mappings.
|
||||
*/
|
||||
public static final class ApplicationMappingsDescriptor {
|
||||
public static final class ApplicationMappingsDescriptor implements OperationResponseBody {
|
||||
|
||||
private final Map<String, ContextMappingsDescriptor> contextMappings;
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link OperationResponseBody}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OperationResponseBodyTests {
|
||||
|
||||
@Test
|
||||
void ofMapReturnsOperationResponseBody() {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put("one", "1");
|
||||
map.put("two", "2");
|
||||
Map<String, String> mapDescriptor = OperationResponseBody.of(map);
|
||||
assertThat(mapDescriptor).containsExactly(entry("one", "1"), entry("two", "2"));
|
||||
assertThat(mapDescriptor).isInstanceOf(OperationResponseBody.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -68,19 +68,19 @@ class IntegrationGraphEndpointTests {
|
||||
|
||||
@Test
|
||||
void readOperationShouldReturnGraph() {
|
||||
Graph mockedGraph = mock(Graph.class);
|
||||
Graph graph = mock(Graph.class);
|
||||
Map<String, Object> contentDescriptor = new LinkedHashMap<>();
|
||||
Collection<IntegrationNode> nodes = new ArrayList<>();
|
||||
Collection<LinkNode> links = new ArrayList<>();
|
||||
given(mockedGraph.getContentDescriptor()).willReturn(contentDescriptor);
|
||||
given(mockedGraph.getNodes()).willReturn(nodes);
|
||||
given(mockedGraph.getLinks()).willReturn(links);
|
||||
given(this.server.getGraph()).willReturn(mockedGraph);
|
||||
GraphDescriptor graph = this.endpoint.graph();
|
||||
given(graph.getContentDescriptor()).willReturn(contentDescriptor);
|
||||
given(graph.getNodes()).willReturn(nodes);
|
||||
given(graph.getLinks()).willReturn(links);
|
||||
given(this.server.getGraph()).willReturn(graph);
|
||||
GraphDescriptor descriptor = this.endpoint.graph();
|
||||
then(this.server).should().getGraph();
|
||||
assertThat(graph.getContentDescriptor()).isSameAs(contentDescriptor);
|
||||
assertThat(graph.getNodes()).isSameAs(nodes);
|
||||
assertThat(graph.getLinks()).isSameAs(links);
|
||||
assertThat(descriptor.getContentDescriptor()).isSameAs(contentDescriptor);
|
||||
assertThat(descriptor.getNodes()).isSameAs(nodes);
|
||||
assertThat(descriptor.getLinks()).isSameAs(links);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthContributor;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@@ -32,10 +33,6 @@ import org.springframework.context.annotation.Bean;
|
||||
@ConfigurationPropertiesScan
|
||||
public class SampleActuatorApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleActuatorApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HealthIndicator helloHealthIndicator() {
|
||||
return createHealthIndicator("world");
|
||||
@@ -61,4 +58,10 @@ public class SampleActuatorApplication {
|
||||
return () -> Health.up().withDetail("hello", value).build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication application = new SpringApplication(SampleActuatorApplication.class);
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(1024));
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,3 +28,5 @@ management.endpoint.health.group.comp.show-details=always
|
||||
|
||||
management.endpoints.migrate-legacy-ids=true
|
||||
|
||||
management.endpoints.jackson.isolated-object-mapper=true
|
||||
spring.jackson.visibility.field=any
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.actuator;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader;
|
||||
|
||||
class ApplicationStartupSpringBootContextLoader extends SpringBootContextLoader {
|
||||
|
||||
@Override
|
||||
protected SpringApplication getSpringApplication() {
|
||||
SpringApplication application = new SpringApplication();
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(1024));
|
||||
return application;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.actuator;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test for WebMVC actuator when using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=false")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
class SampleActuatorApplicationIsolatedObjectMapperFalseTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void resourceShouldBeAvailableOnMainPort() {
|
||||
ResponseEntity<String> entity = this.testRestTemplate.withBasicAuth("user", "password")
|
||||
.getForEntity("/actuator/startup", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.actuator;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test for WebMVC actuator when using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=true")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
class SampleActuatorApplicationIsolatedObjectMapperTrueTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void resourceShouldBeAvailableOnMainPort() {
|
||||
ResponseEntity<String> entity = this.testRestTemplate.withBasicAuth("user", "password")
|
||||
.getForEntity("/actuator/startup", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(entity.getBody()).contains("\"timeline\":");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,13 +18,15 @@ package smoketest.jersey;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleJerseyApplication extends SpringBootServletInitializer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SampleJerseyApplication().configure(new SpringApplicationBuilder(SampleJerseyApplication.class)).run(args);
|
||||
new SampleJerseyApplication().configure(new SpringApplicationBuilder(SampleJerseyApplication.class)
|
||||
.applicationStartup(new BufferingApplicationStartup(2048))).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoints.jackson.isolated-object-mapper=true
|
||||
spring.jackson.visibility.field=any
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.jersey;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader;
|
||||
|
||||
class ApplicationStartupSpringBootContextLoader extends SpringBootContextLoader {
|
||||
|
||||
@Override
|
||||
protected SpringApplication getSpringApplication() {
|
||||
SpringApplication application = new SpringApplication();
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(1024));
|
||||
return application;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.jersey;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test for Jersey actuator when not using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=false")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
public class JerseyActuatorIsolatedObjectMapperFalseTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@LocalManagementPort
|
||||
private int managementPort;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void resourceShouldBeAvailableOnMainPort() {
|
||||
ResponseEntity<String> entity = this.testRestTemplate
|
||||
.getForEntity("http://localhost:" + this.port + "/actuator/startup", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(entity.getBody())
|
||||
.contains("Java 8 date/time type `java.time.Clock$SystemClock` not supported by default");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.jersey;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.server.LocalManagementPort;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test for Jersey actuator when using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=true")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
public class JerseyActuatorIsolatedObjectMapperTrueTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@LocalManagementPort
|
||||
private int managementPort;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate testRestTemplate;
|
||||
|
||||
@Test
|
||||
void resourceShouldBeAvailableOnMainPort() {
|
||||
ResponseEntity<String> entity = this.testRestTemplate
|
||||
.getForEntity("http://localhost:" + this.port + "/actuator/startup", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(entity.getBody()).contains("\"timeline\":");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package smoketest.webflux;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
@@ -29,7 +30,9 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
||||
public class SampleWebFluxApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleWebFluxApplication.class, args);
|
||||
SpringApplication application = new SpringApplication(SampleWebFluxApplication.class);
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(1024));
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoints.jackson.isolated-object-mapper=true
|
||||
spring.jackson.visibility.field=any
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.webflux;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader;
|
||||
|
||||
class ApplicationStartupSpringBootContextLoader extends SpringBootContextLoader {
|
||||
|
||||
@Override
|
||||
protected SpringApplication getSpringApplication() {
|
||||
SpringApplication application = new SpringApplication();
|
||||
application.setApplicationStartup(new BufferingApplicationStartup(1024));
|
||||
return application;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = { "management.server.port=0", "management.endpoints.web.base-path=/" })
|
||||
class WebFluxDifferentPortSampleActuatorApplicationTests {
|
||||
class SampleWebFluxApplicationActuatorDifferentPortTests {
|
||||
|
||||
@LocalManagementPort
|
||||
private int managementPort;
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.webflux;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
/**
|
||||
* Integration test for WebFlux actuator when using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=false")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
class SampleWebFluxApplicationActuatorIsolatedObjectMapperFalseTests {
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webClient;
|
||||
|
||||
@Test
|
||||
void linksEndpointShouldBeAvailable() {
|
||||
this.webClient.get().uri("/actuator/startup").accept(MediaType.APPLICATION_JSON).exchange().expectStatus()
|
||||
.is5xxServerError();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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 smoketest.webflux;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.web.reactive.server.EntityExchangeResult;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test for WebFlux actuator when not using an isolated {@link ObjectMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = "management.endpoints.jackson.isolated-object-mapper=true")
|
||||
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
|
||||
class SampleWebFluxApplicationActuatorIsolatedObjectMapperTrueTests {
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webClient;
|
||||
|
||||
@Test
|
||||
void linksEndpointShouldBeAvailable() {
|
||||
this.webClient.get().uri("/actuator/startup").accept(MediaType.APPLICATION_JSON).exchange().expectStatus()
|
||||
.isOk().expectBody().consumeWith(this::assertExpectedJson);
|
||||
}
|
||||
|
||||
private void assertExpectedJson(EntityExchangeResult<byte[]> result) {
|
||||
String body = new String(result.getResponseBody(), StandardCharsets.UTF_8);
|
||||
assertThat(body).contains("\"timeline\":");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user