Revert "Add actuator specific ObjectMapper"

See gh-12951
See gh-20291
pull/20294/head
Brian Clozel 5 years ago
parent 42492026a9
commit ab72cc8fdb

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,7 +25,6 @@ import org.springframework.boot.actuate.endpoint.annotation.EndpointConverter;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper; import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper; import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor; import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.convert.ApplicationConversionService; import org.springframework.boot.convert.ApplicationConversionService;
@ -76,9 +75,4 @@ public class EndpointAutoConfiguration {
return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment)); return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));
} }
@Bean
public ActuatorJsonMapperProvider actuatorJsonMapperProvider() {
return new ActuatorJsonMapperProvider();
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import java.util.stream.Collectors;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter; import org.springframework.boot.actuate.autoconfigure.endpoint.ExposeExcludePropertyEndpointFilter;
import org.springframework.boot.actuate.endpoint.EndpointFilter; import org.springframework.boot.actuate.endpoint.EndpointFilter;
@ -33,7 +35,6 @@ import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointExporter;
import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointsSupplier; import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.jmx.JmxOperationResponseMapper; import org.springframework.boot.actuate.endpoint.jmx.JmxOperationResponseMapper;
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -84,12 +85,12 @@ public class JmxEndpointAutoConfiguration {
@Bean @Bean
@ConditionalOnSingleCandidate(MBeanServer.class) @ConditionalOnSingleCandidate(MBeanServer.class)
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer, Environment environment, public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer, Environment environment,
ActuatorJsonMapperProvider actuatorJsonMapperProvider, JmxEndpointsSupplier jmxEndpointsSupplier) { ObjectProvider<ObjectMapper> objectMapper, JmxEndpointsSupplier jmxEndpointsSupplier) {
String contextId = ObjectUtils.getIdentityHexString(this.applicationContext); String contextId = ObjectUtils.getIdentityHexString(this.applicationContext);
EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(this.properties, environment, EndpointObjectNameFactory objectNameFactory = new DefaultEndpointObjectNameFactory(this.properties, environment,
mBeanServer, contextId); mBeanServer, contextId);
JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper( JmxOperationResponseMapper responseMapper = new JacksonJmxOperationResponseMapper(
actuatorJsonMapperProvider.getInstance()); objectMapper.getIfAvailable());
return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper, return new JmxEndpointExporter(mBeanServer, objectNameFactory, responseMapper,
jmxEndpointsSupplier.getEndpoints()); jmxEndpointsSupplier.getEndpoints());

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,10 +22,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.Resource;
@ -35,8 +32,6 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfi
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@ -89,12 +84,6 @@ class JerseyWebEndpointManagementContextConfiguration {
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT); || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT);
} }
@Bean
ResourceConfigCustomizer actuatorResourceConfigCustomizer(ActuatorJsonMapperProvider jsonMapperProvider) {
return (ResourceConfig config) -> config.register(
new ActuatorJsonMapperContextResolver(jsonMapperProvider.getInstance()), ContextResolver.class);
}
/** /**
* Register endpoints with the {@link ResourceConfig}. The * Register endpoints with the {@link ResourceConfig}. The
* {@link ResourceConfigCustomizer} cannot be used because we don't want to apply * {@link ResourceConfigCustomizer} cannot be used because we don't want to apply
@ -156,20 +145,4 @@ class JerseyWebEndpointManagementContextConfiguration {
} }
@Produces({ ActuatorMediaType.V3_JSON, ActuatorMediaType.V2_JSON })
private static final class ActuatorJsonMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper objectMapper;
private ActuatorJsonMapperContextResolver(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public ObjectMapper getContext(Class<?> type) {
return this.objectMapper;
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,8 +26,6 @@ import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfi
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@ -42,13 +40,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.DispatcherHandler;
@ -59,7 +52,6 @@ import org.springframework.web.reactive.DispatcherHandler;
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel
* @since 2.0.0 * @since 2.0.0
*/ */
@ManagementContextConfiguration(proxyBeanMethods = false) @ManagementContextConfiguration(proxyBeanMethods = false)
@ -101,16 +93,4 @@ public class WebFluxEndpointManagementContextConfiguration {
corsProperties.toCorsConfiguration()); corsProperties.toCorsConfiguration());
} }
@Bean
@Order(-1)
public CodecCustomizer actuatorJsonCodec(ActuatorJsonMapperProvider actuatorJsonMapperProvider) {
return (configurer) -> {
MediaType v3MediaType = MediaType.parseMediaType(ActuatorMediaType.V3_JSON);
MediaType v2MediaType = MediaType.parseMediaType(ActuatorMediaType.V2_JSON);
CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
customCodecs.register(
new Jackson2JsonEncoder(actuatorJsonMapperProvider.getInstance(), v3MediaType, v2MediaType));
};
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,16 +20,12 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@ -46,15 +42,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Spring MVC * {@link ManagementContextConfiguration @ManagementContextConfiguration} for Spring MVC
@ -101,35 +91,4 @@ public class WebMvcEndpointManagementContextConfiguration {
corsProperties.toCorsConfiguration()); corsProperties.toCorsConfiguration());
} }
@Configuration(proxyBeanMethods = false)
public static class JsonWebMvcConfigurer implements WebMvcConfigurer, Ordered {
private final ActuatorJsonMapperProvider actuatorJsonMapperProvider;
public JsonWebMvcConfigurer(ActuatorJsonMapperProvider objectMapperFactory) {
this.actuatorJsonMapperProvider = objectMapperFactory;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ActuatorJsonHttpMessageConverter(this.actuatorJsonMapperProvider.getInstance()));
}
// WebMvcAutoConfiguration is ordered at 0
@Override
public int getOrder() {
return -1;
}
}
static class ActuatorJsonHttpMessageConverter extends AbstractJackson2HttpMessageConverter {
ActuatorJsonHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.parseMediaType(ActuatorMediaType.V3_JSON),
MediaType.parseMediaType(ActuatorMediaType.V2_JSON));
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,9 @@
package org.springframework.boot.actuate.autoconfigure.web.servlet; package org.springframework.boot.actuate.autoconfigure.web.servlet;
import java.util.List;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
import org.springframework.boot.actuate.endpoint.json.ActuatorJsonMapperProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -37,15 +33,11 @@ import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContextListener; import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.filter.RequestContextFilter;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
* {@link ManagementContextConfiguration @ManagementContextConfiguration} for Spring MVC * {@link ManagementContextConfiguration @ManagementContextConfiguration} for Spring MVC
@ -116,29 +108,6 @@ class WebMvcEndpointChildContextConfiguration {
return new OrderedRequestContextFilter(); return new OrderedRequestContextFilter();
} }
/**
* Since {@code WebMvcEndpointManagementContextConfiguration} is adding an
* actuator-specific JSON message converter, {@code @EnableWebMvc} will not register
* default converters. We need to register a JSON converter for plain
* {@code "application/json"} still.
* WebMvcEndpointChildContextConfigurationIntegrationTests
*/
@Configuration(proxyBeanMethods = false)
public static class FallbackJsonConverterConfigurer implements WebMvcConfigurer {
private final ActuatorJsonMapperProvider actuatorJsonMapperProvider;
FallbackJsonConverterConfigurer(ObjectProvider<ActuatorJsonMapperProvider> objectMapperSupplier) {
this.actuatorJsonMapperProvider = objectMapperSupplier.getIfAvailable(ActuatorJsonMapperProvider::new);
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(this.actuatorJsonMapperProvider.getInstance()));
}
}
/** /**
* {@link WebServerFactoryCustomizer} to add an {@link ErrorPage} so that the * {@link WebServerFactoryCustomizer} to add an {@link ErrorPage} so that the
* {@link ManagementErrorEndpoint} can be used. * {@link ManagementErrorEndpoint} can be used.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -52,7 +52,7 @@ class ScheduledTasksEndpointDocumentationTests extends MockMvcEndpointDocumentat
@Test @Test
void scheduledTasks() throws Exception { void scheduledTasks() throws Exception {
this.mockMvc.perform(get("/actuator/scheduledtasks").accept("application/json")).andExpect(status().isOk()) this.mockMvc.perform(get("/actuator/scheduledtasks")).andExpect(status().isOk())
.andDo(document("scheduled-tasks", .andDo(document("scheduled-tasks",
preprocessResponse(replacePattern( preprocessResponse(replacePattern(
Pattern.compile("org.*\\.ScheduledTasksEndpointDocumentationTests\\$TestConfiguration"), Pattern.compile("org.*\\.ScheduledTasksEndpointDocumentationTests\\$TestConfiguration"),

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import java.util.Collections;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration.JerseyWebEndpointsResourcesRegistrar; import org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration.JerseyWebEndpointsResourcesRegistrar;
import org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration;
@ -42,8 +41,8 @@ import static org.assertj.core.api.Assertions.assertThat;
class JerseyWebEndpointManagementContextConfigurationTests { class JerseyWebEndpointManagementContextConfigurationTests {
private final WebApplicationContextRunner runner = new WebApplicationContextRunner() private final WebApplicationContextRunner runner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class, .withConfiguration(AutoConfigurations.of(WebEndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class, JerseyWebEndpointManagementContextConfiguration.class)) JerseyWebEndpointManagementContextConfiguration.class))
.withBean(WebEndpointsSupplier.class, () -> Collections::emptyList); .withBean(WebEndpointsSupplier.class, () -> Collections::emptyList);
@Test @Test

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import java.util.function.Supplier;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
@ -32,23 +33,32 @@ import org.springframework.boot.actuate.endpoint.web.EndpointServlet;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint; import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint; import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
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.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.startsWith; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -57,39 +67,98 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* Integration tests for the Actuator's MVC endpoints. * Integration tests for the Actuator's MVC endpoints.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Brian Clozel
*/ */
public class WebMvcEndpointIntegrationTests { class WebMvcEndpointIntegrationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() private AnnotationConfigServletWebApplicationContext context;
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class, GsonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, @AfterEach
WebEndpointAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, void close() {
AuditAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, TestSecurityContextHolder.clearContext();
WebMvcAutoConfiguration.class, ManagementContextAutoConfiguration.class, this.context.close();
AuditAutoConfiguration.class, DispatcherServletAutoConfiguration.class, }
BeansEndpointAutoConfiguration.class));
@Test @Test
void linksAreProvidedToAllEndpointTypes() throws Exception { void endpointsAreSecureByDefault() throws Exception {
this.contextRunner.withUserConfiguration(EndpointsConfiguration.class) this.context = new AnnotationConfigServletWebApplicationContext();
.withPropertyValues("management.endpoints.web.exposure.include=*").run((context) -> { this.context.register(SecureConfiguration.class);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/actuator").accept("*/*")).andExpect(status().isOk()) mockMvc.perform(get("/actuator/beans").accept(MediaType.APPLICATION_JSON)).andExpect(status().isUnauthorized());
.andExpect(jsonPath("_links", both(hasKey("beans")).and(hasKey("servlet"))
.and(hasKey("restcontroller")).and(hasKey("controller"))));
});
} }
@Test @Test
void dedicatedJsonMapperIsUsed() throws Exception { void endpointsAreSecureByDefaultWithCustomBasePath() throws Exception {
this.contextRunner.withPropertyValues("spring.mvc.converters.preferred-json-mapper:gson", this.context = new AnnotationConfigServletWebApplicationContext();
"management.endpoints.web.exposure.include=*").run((context) -> { this.context.register(SecureConfiguration.class);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); TestPropertyValues.of("management.endpoints.web.base-path:/management").applyTo(this.context);
mockMvc.perform(get("/actuator/beans").accept("*/*")).andExpect(status().isOk()) MockMvc mockMvc = createSecureMockMvc();
.andExpect(MockMvcResultMatchers.header().string("Content-Type", mockMvc.perform(get("/management/beans").accept(MediaType.APPLICATION_JSON))
startsWith("application/vnd.spring-boot.actuator"))); .andExpect(status().isUnauthorized());
}); }
@Test
void endpointsAreSecureWithActuatorRoleWithCustomBasePath() throws Exception {
TestSecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
TestPropertyValues
.of("management.endpoints.web.base-path:/management", "management.endpoints.web.exposure.include=*")
.applyTo(this.context);
MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/management/beans")).andExpect(status().isOk());
}
@Test
void linksAreProvidedToAllEndpointTypes() throws Exception {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(DefaultConfiguration.class, EndpointsConfiguration.class);
TestPropertyValues.of("management.endpoints.web.exposure.include=*").applyTo(this.context);
MockMvc mockMvc = doCreateMockMvc();
mockMvc.perform(get("/actuator").accept("*/*")).andExpect(status().isOk()).andExpect(jsonPath("_links",
both(hasKey("beans")).and(hasKey("servlet")).and(hasKey("restcontroller")).and(hasKey("controller"))));
}
private MockMvc createSecureMockMvc() {
return doCreateMockMvc(springSecurity());
}
private MockMvc doCreateMockMvc(MockMvcConfigurer... configurers) {
this.context.setServletContext(new MockServletContext());
this.context.refresh();
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.context);
for (MockMvcConfigurer configurer : configurers) {
builder.apply(configurer);
}
return builder.build();
}
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class, AuditAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementContextAutoConfiguration.class, AuditAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, BeansEndpointAutoConfiguration.class })
static class DefaultConfiguration {
}
@Import(SecureConfiguration.class)
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class })
static class SpringHateoasConfiguration {
}
@Import(SecureConfiguration.class)
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
static class SpringDataRestConfiguration {
}
@Import(DefaultConfiguration.class)
@ImportAutoConfiguration({ SecurityAutoConfiguration.class })
static class SecureConfiguration {
} }
@ServletEndpoint(id = "servlet") @ServletEndpoint(id = "servlet")

@ -1,127 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
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.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for the Actuator's MVC endpoints security features.
*
* @author Andy Wilkinson
*/
class WebMvcEndpointSecurityIntegrationTests {
private AnnotationConfigServletWebApplicationContext context;
@AfterEach
void close() {
TestSecurityContextHolder.clearContext();
this.context.close();
}
@Test
void endpointsAreSecureByDefault() throws Exception {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/actuator/beans").accept(MediaType.APPLICATION_JSON)).andExpect(status().isUnauthorized());
}
@Test
void endpointsAreSecureByDefaultWithCustomBasePath() throws Exception {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
TestPropertyValues.of("management.endpoints.web.base-path:/management").applyTo(this.context);
MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/management/beans").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isUnauthorized());
}
@Test
void endpointsAreSecureWithActuatorRoleWithCustomBasePath() throws Exception {
TestSecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
TestPropertyValues
.of("management.endpoints.web.base-path:/management", "management.endpoints.web.exposure.include=*")
.applyTo(this.context);
MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/management/beans")).andExpect(status().isOk());
}
private MockMvc createSecureMockMvc() {
return doCreateMockMvc(springSecurity());
}
private MockMvc doCreateMockMvc(MockMvcConfigurer... configurers) {
this.context.setServletContext(new MockServletContext());
this.context.refresh();
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.context);
for (MockMvcConfigurer configurer : configurers) {
builder.apply(configurer);
}
return builder.build();
}
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class, AuditAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementContextAutoConfiguration.class, AuditAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, BeansEndpointAutoConfiguration.class })
static class DefaultConfiguration {
}
@Import(DefaultConfiguration.class)
@ImportAutoConfiguration({ SecurityAutoConfiguration.class })
static class SecureConfiguration {
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -59,7 +59,6 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
WebClient client = WebClient.create("http://localhost:" + port); WebClient client = WebClient.create("http://localhost:" + port);
ClientResponse response = client.get().uri("actuator/fail").accept(MediaType.APPLICATION_JSON) ClientResponse response = client.get().uri("actuator/fail").accept(MediaType.APPLICATION_JSON)
.exchange().block(); .exchange().block();
assertThat(response.headers().contentType().get()).isEqualTo(MediaType.APPLICATION_JSON);
assertThat(response.bodyToMono(String.class).block()).contains("message\":\"Epic Fail"); assertThat(response.bodyToMono(String.class).block()).contains("message\":\"Epic Fail");
}); });
} }

@ -1,49 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.json;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
/**
* Factory for an {@code ObjectMapper} instance to be shared within Actuator
* infrastructure.
*
* <p>
* The goal is to have a Jackson configuration separate from the rest of the application
* to keep a consistent serialization behavior between applications.
*
* @author Brian Clozel
* @since 2.3.0
*/
public class ActuatorJsonMapperProvider {
private ObjectMapper objectMapper;
public ObjectMapper getInstance() {
if (this.objectMapper == null) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
builder.featuresToDisable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS);
this.objectMapper = builder.build();
}
return this.objectMapper;
}
}

@ -1,20 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* JSON support for actuator endpoints.
*/
package org.springframework.boot.actuate.endpoint.json;

@ -46,9 +46,6 @@
<disallow pkg="org.springframework.web.servlet" /> <disallow pkg="org.springframework.web.servlet" />
</subpackage> </subpackage>
</subpackage> </subpackage>
<subpackage name="json">
<allow pkg="org.springframework.http.converter" />
</subpackage>
</subpackage> </subpackage>
<!-- Logging --> <!-- Logging -->

Loading…
Cancel
Save