Commit 0af45363 authored by Andy Wilkinson's avatar Andy Wilkinson

Ensure media types are used consistently across endpoint mappings

Previously, the media types that are consumed and produced by
endpoints were configured in the web stack-specific configuration.
Furthermore, these configured media types were not used for the
discovery "endpoint" that links to all the available endpoints.

This commit introduces EndpointMediaTypes that is configred in a
single, central location and then used to configure the consumed and
produced media types for endpoints exposed via WebFlux, Web MVC, and
Jersey as well as the discovery "endpoint" provided by each.

Closes gh-10659
parent 4c5c5105
...@@ -20,6 +20,7 @@ import java.util.Arrays; ...@@ -20,6 +20,7 @@ import java.util.Arrays;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -67,11 +68,12 @@ public class CloudFoundryActuatorAutoConfiguration { ...@@ -67,11 +68,12 @@ public class CloudFoundryActuatorAutoConfiguration {
@Bean @Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping( public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
EndpointProvider<WebEndpointOperation> provider, Environment environment, EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes, Environment environment,
RestTemplateBuilder builder) { RestTemplateBuilder builder) {
return new CloudFoundryWebEndpointServletHandlerMapping( return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cloudfoundryapplication"), new EndpointMapping("/cloudfoundryapplication"),
provider.getEndpoints(), getCorsConfiguration(), provider.getEndpoints(), endpointMediaTypes, getCorsConfiguration(),
getSecurityInterceptor(builder, environment)); getSecurityInterceptor(builder, environment));
} }
......
...@@ -36,6 +36,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker; ...@@ -36,6 +36,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException; import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException; import org.springframework.boot.actuate.endpoint.ParametersMissingException;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
...@@ -76,9 +77,9 @@ class CloudFoundryWebEndpointServletHandlerMapping ...@@ -76,9 +77,9 @@ class CloudFoundryWebEndpointServletHandlerMapping
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping, CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints, Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration, EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
CloudFoundrySecurityInterceptor securityInterceptor) { CloudFoundrySecurityInterceptor securityInterceptor) {
super(endpointMapping, webEndpoints, corsConfiguration); super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration);
this.securityInterceptor = securityInterceptor; this.securityInterceptor = securityInterceptor;
} }
......
...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; ...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -71,6 +72,11 @@ public class EndpointAutoConfiguration { ...@@ -71,6 +72,11 @@ public class EndpointAutoConfiguration {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }
@Bean
public EndpointMediaTypes endpointMediaTypes() {
return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES);
}
@Bean @Bean
public EndpointProvider<WebEndpointOperation> webEndpointProvider( public EndpointProvider<WebEndpointOperation> webEndpointProvider(
OperationParameterMapper parameterMapper, OperationParameterMapper parameterMapper,
...@@ -78,7 +84,7 @@ public class EndpointAutoConfiguration { ...@@ -78,7 +84,7 @@ public class EndpointAutoConfiguration {
Environment environment = this.applicationContext.getEnvironment(); Environment environment = this.applicationContext.getEnvironment();
WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer( WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext, parameterMapper, cachingConfigurationFactory, this.applicationContext, parameterMapper, cachingConfigurationFactory,
MEDIA_TYPES, MEDIA_TYPES); endpointMediaTypes());
return new EndpointProvider<>(environment, endpointDiscoverer, return new EndpointProvider<>(environment, endpointDiscoverer,
EndpointExposure.WEB); EndpointExposure.WEB);
} }
......
...@@ -26,6 +26,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathP ...@@ -26,6 +26,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathP
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.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory; import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
...@@ -55,11 +56,12 @@ class JerseyWebEndpointManagementContextConfiguration { ...@@ -55,11 +56,12 @@ class JerseyWebEndpointManagementContextConfiguration {
@Bean @Bean
public ResourceConfigCustomizer webEndpointRegistrar( public ResourceConfigCustomizer webEndpointRegistrar(
EndpointProvider<WebEndpointOperation> provider, EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
WebEndpointProperties webEndpointProperties) { WebEndpointProperties webEndpointProperties) {
return (resourceConfig) -> resourceConfig.registerResources( return (resourceConfig) -> resourceConfig.registerResources(
new HashSet<>(new JerseyEndpointResourceFactory().createEndpointResources( new HashSet<>(new JerseyEndpointResourceFactory().createEndpointResources(
new EndpointMapping(webEndpointProperties.getBasePath()), new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints()))); provider.getEndpoints(), endpointMediaTypes)));
} }
@Bean @Bean
......
...@@ -22,6 +22,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathP ...@@ -22,6 +22,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathP
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.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -45,10 +46,11 @@ public class WebFluxEndpointManagementContextConfiguration { ...@@ -45,10 +46,11 @@ public class WebFluxEndpointManagementContextConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping( public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(
EndpointProvider<WebEndpointOperation> provider, EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
WebEndpointProperties webEndpointProperties) { WebEndpointProperties webEndpointProperties) {
return new WebFluxEndpointHandlerMapping( return new WebFluxEndpointHandlerMapping(
new EndpointMapping(webEndpointProperties.getBasePath()), new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints()); provider.getEndpoints(), endpointMediaTypes);
} }
@Bean @Bean
......
...@@ -23,6 +23,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointPr ...@@ -23,6 +23,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointPr
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
...@@ -56,11 +57,12 @@ public class WebMvcEndpointManagementContextConfiguration { ...@@ -56,11 +57,12 @@ public class WebMvcEndpointManagementContextConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping( public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
EndpointProvider<WebEndpointOperation> provider, EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties, CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) { WebEndpointProperties webEndpointProperties) {
WebMvcEndpointHandlerMapping handlerMapping = new WebMvcEndpointHandlerMapping( WebMvcEndpointHandlerMapping handlerMapping = new WebMvcEndpointHandlerMapping(
new EndpointMapping(webEndpointProperties.getBasePath()), new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints(), getCorsConfiguration(corsProperties)); provider.getEndpoints(), endpointMediaTypes, getCorsConfiguration(corsProperties));
return handlerMapping; return handlerMapping;
} }
......
...@@ -25,6 +25,7 @@ import org.junit.Test; ...@@ -25,6 +25,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
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;
...@@ -40,11 +41,15 @@ import org.springframework.mock.web.MockServletContext; ...@@ -40,11 +41,15 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/** /**
* Tests for {@link CloudFoundryActuatorAutoConfiguration}. * Tests for {@link CloudFoundryActuatorAutoConfiguration}.
...@@ -92,6 +97,18 @@ public class CloudFoundryActuatorAutoConfigurationTests { ...@@ -92,6 +97,18 @@ public class CloudFoundryActuatorAutoConfigurationTests {
Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type")); Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type"));
} }
@Test
public void cloudfoundryapplicationProducesActuatorMediaType() throws Exception {
TestPropertyValues
.of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
"vcap.application.cf_api:http://my-cloud-controller.com")
.applyTo(this.context);
this.context.refresh();
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
mockMvc.perform(get("/cloudfoundryapplication")).andExpect(header()
.string("Content-Type", ActuatorMediaType.V2_JSON + ";charset=UTF-8"));
}
@Test @Test
public void cloudFoundryPlatformActiveSetsApplicationId() throws Exception { public void cloudFoundryPlatformActiveSetsApplicationId() throws Exception {
CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping(); CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping();
......
...@@ -31,6 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector; ...@@ -31,6 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
...@@ -190,28 +191,35 @@ public class CloudFoundryMvcWebEndpointIntegrationTests { ...@@ -190,28 +191,35 @@ public class CloudFoundryMvcWebEndpointIntegrationTests {
"app-id"); "app-id");
} }
@Bean
public EndpointMediaTypes EndpointMediaTypes() {
return new EndpointMediaTypes(Collections.singletonList("application/json"),
Collections.singletonList("application/json"));
}
@Bean @Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping( public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
WebAnnotationEndpointDiscoverer webEndpointDiscoverer, WebAnnotationEndpointDiscoverer webEndpointDiscoverer,
EndpointMediaTypes endpointMediaTypes,
CloudFoundrySecurityInterceptor interceptor) { CloudFoundrySecurityInterceptor interceptor) {
CorsConfiguration corsConfiguration = new CorsConfiguration(); CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com")); corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new CloudFoundryWebEndpointServletHandlerMapping( return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cfApplication"), new EndpointMapping("/cfApplication"),
webEndpointDiscoverer.discoverEndpoints(), corsConfiguration, webEndpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
interceptor); corsConfiguration, interceptor);
} }
@Bean @Bean
public WebAnnotationEndpointDiscoverer webEndpointDiscoverer( public WebAnnotationEndpointDiscoverer webEndpointDiscoverer(
ApplicationContext applicationContext) { ApplicationContext applicationContext,
EndpointMediaTypes endpointMediaTypes) {
OperationParameterMapper parameterMapper = new ConversionServiceOperationParameterMapper( OperationParameterMapper parameterMapper = new ConversionServiceOperationParameterMapper(
DefaultConversionService.getSharedInstance()); DefaultConversionService.getSharedInstance());
return new WebAnnotationEndpointDiscoverer(applicationContext, return new WebAnnotationEndpointDiscoverer(applicationContext,
parameterMapper, (id) -> new CachingConfiguration(0), parameterMapper, (id) -> new CachingConfiguration(0),
Collections.singletonList("application/json"), endpointMediaTypes);
Collections.singletonList("application/json"));
} }
@Bean @Bean
......
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link EndpointAutoConfiguration}.
*
* @author Andy Wilkinson
*/
public class EndpointAutoConfigurationTests {
@Test
public void webApplicationConfiguresEndpointMediaTypes() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(EndpointAutoConfiguration.class))
.run((context) -> {
EndpointMediaTypes endpointMediaTypes = context
.getBean(EndpointMediaTypes.class);
assertThat(endpointMediaTypes.getConsumed()).containsExactly(
ActuatorMediaType.V2_JSON, "application/json");
});
}
}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.actuate.autoconfigure.web.servlet; package org.springframework.boot.actuate.autoconfigure.web.servlet;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
...@@ -24,6 +25,7 @@ import org.junit.Test; ...@@ -24,6 +25,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.endpoint.DefaultEnablement; import org.springframework.boot.actuate.endpoint.DefaultEnablement;
import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.EndpointInfo;
import org.springframework.boot.actuate.endpoint.OperationType; import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
...@@ -142,7 +144,9 @@ public class RequestMappingEndpointTests { ...@@ -142,7 +144,9 @@ public class RequestMappingEndpointTests {
WebMvcEndpointHandlerMapping mapping = new WebMvcEndpointHandlerMapping( WebMvcEndpointHandlerMapping mapping = new WebMvcEndpointHandlerMapping(
new EndpointMapping("application"), new EndpointMapping("application"),
Collections.singleton(new EndpointInfo<>("test", Collections.singleton(new EndpointInfo<>("test",
DefaultEnablement.ENABLED, Collections.singleton(operation)))); DefaultEnablement.ENABLED, Collections.singleton(operation))),
new EndpointMediaTypes(Arrays.asList("application/vnd.test+json"),
Arrays.asList("application/vnd.test+json")));
mapping.setApplicationContext(new StaticApplicationContext()); mapping.setApplicationContext(new StaticApplicationContext());
mapping.afterPropertiesSet(); mapping.afterPropertiesSet();
return mapping; return mapping;
......
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.web;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
/**
* Media types that are, by default, produced and consumed by an endpoint.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public class EndpointMediaTypes {
private final List<String> produced;
private final List<String> consumed;
/**
* Creates a new {@link EndpointMediaTypes} with the given {@code produced} and
* {@code consumed} media types.
* @param produced the default media types that are produced by an endpoint. Must not
* be {@code null}.
* @param consumed the default media types that are consumed by an endpoint. Must not
*/
public EndpointMediaTypes(List<String> produced, List<String> consumed) {
Assert.notNull(produced, () -> "Produced must not be null");
Assert.notNull(consumed, () -> "Consumed must not be null");
this.produced = Collections.unmodifiableList(produced);
this.consumed = Collections.unmodifiableList(consumed);
}
/**
* Returns the media types produced by an endpoint.
*
* @return the produced media types
*/
public List<String> getProduced() {
return this.produced;
}
/**
* Returns the media types consumed by an endpoint.
*
* @return the consumed media types
*/
public List<String> getConsumed() {
return this.consumed;
}
}
...@@ -39,6 +39,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector; ...@@ -39,6 +39,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration; import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration;
import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory; import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory;
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker; import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
...@@ -68,17 +69,16 @@ public class WebAnnotationEndpointDiscoverer extends ...@@ -68,17 +69,16 @@ public class WebAnnotationEndpointDiscoverer extends
* @param operationParameterMapper the {@link OperationParameterMapper} used to * @param operationParameterMapper the {@link OperationParameterMapper} used to
* convert arguments when an operation is invoked * convert arguments when an operation is invoked
* @param cachingConfigurationFactory the {@link CachingConfiguration} factory to use * @param cachingConfigurationFactory the {@link CachingConfiguration} factory to use
* @param consumedMediaTypes the media types consumed by web endpoint operations * @param endpointMediaTypes the media types produced and consumed by web endpoint
* @param producedMediaTypes the media types produced by web endpoint operations * operations
*/ */
public WebAnnotationEndpointDiscoverer(ApplicationContext applicationContext, public WebAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
OperationParameterMapper operationParameterMapper, OperationParameterMapper operationParameterMapper,
CachingConfigurationFactory cachingConfigurationFactory, CachingConfigurationFactory cachingConfigurationFactory,
Collection<String> consumedMediaTypes, EndpointMediaTypes endpointMediaTypes) {
Collection<String> producedMediaTypes) {
super(applicationContext, super(applicationContext,
new WebEndpointOperationFactory(operationParameterMapper, new WebEndpointOperationFactory(operationParameterMapper,
consumedMediaTypes, producedMediaTypes), endpointMediaTypes),
WebEndpointOperation::getRequestPredicate, cachingConfigurationFactory); WebEndpointOperation::getRequestPredicate, cachingConfigurationFactory);
} }
...@@ -119,16 +119,12 @@ public class WebAnnotationEndpointDiscoverer extends ...@@ -119,16 +119,12 @@ public class WebAnnotationEndpointDiscoverer extends
private final OperationParameterMapper parameterMapper; private final OperationParameterMapper parameterMapper;
private final Collection<String> consumedMediaTypes; private final EndpointMediaTypes endpointMediaTypes;
private final Collection<String> producedMediaTypes;
private WebEndpointOperationFactory(OperationParameterMapper parameterMapper, private WebEndpointOperationFactory(OperationParameterMapper parameterMapper,
Collection<String> consumedMediaTypes, EndpointMediaTypes endpointMediaTypes) {
Collection<String> producedMediaTypes) {
this.parameterMapper = parameterMapper; this.parameterMapper = parameterMapper;
this.consumedMediaTypes = consumedMediaTypes; this.endpointMediaTypes = endpointMediaTypes;
this.producedMediaTypes = producedMediaTypes;
} }
@Override @Override
...@@ -172,7 +168,7 @@ public class WebAnnotationEndpointDiscoverer extends ...@@ -172,7 +168,7 @@ public class WebAnnotationEndpointDiscoverer extends
private Collection<String> determineConsumedMediaTypes( private Collection<String> determineConsumedMediaTypes(
WebEndpointHttpMethod httpMethod, Method method) { WebEndpointHttpMethod httpMethod, Method method) {
if (WebEndpointHttpMethod.POST == httpMethod && consumesRequestBody(method)) { if (WebEndpointHttpMethod.POST == httpMethod && consumesRequestBody(method)) {
return this.consumedMediaTypes; return this.endpointMediaTypes.getConsumed();
} }
return Collections.emptyList(); return Collections.emptyList();
} }
...@@ -189,7 +185,7 @@ public class WebAnnotationEndpointDiscoverer extends ...@@ -189,7 +185,7 @@ public class WebAnnotationEndpointDiscoverer extends
if (producesResourceResponseBody(method)) { if (producesResourceResponseBody(method)) {
return Collections.singletonList("application/octet-stream"); return Collections.singletonList("application/octet-stream");
} }
return this.producedMediaTypes; return this.endpointMediaTypes.getProduced();
} }
private boolean producesResourceResponseBody(Method method) { private boolean producesResourceResponseBody(Method method) {
......
...@@ -27,7 +27,6 @@ import java.util.function.Function; ...@@ -27,7 +27,6 @@ import java.util.function.Function;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
...@@ -43,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker; ...@@ -43,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException; import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException; import org.springframework.boot.actuate.endpoint.ParametersMissingException;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
...@@ -68,18 +68,20 @@ public class JerseyEndpointResourceFactory { ...@@ -68,18 +68,20 @@ public class JerseyEndpointResourceFactory {
* {@code webEndpoints}. * {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints * @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @return the resources for the operations * @return the resources for the operations
*/ */
public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping, public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints) { Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
EndpointMediaTypes endpointMediaTypes) {
List<Resource> resources = new ArrayList<>(); List<Resource> resources = new ArrayList<>();
webEndpoints.stream() webEndpoints.stream()
.flatMap((endpointInfo) -> endpointInfo.getOperations().stream()) .flatMap((endpointInfo) -> endpointInfo.getOperations().stream())
.map((operation) -> createResource(endpointMapping, operation)) .map((operation) -> createResource(endpointMapping, operation))
.forEach(resources::add); .forEach(resources::add);
if (StringUtils.hasText(endpointMapping.getPath())) { if (StringUtils.hasText(endpointMapping.getPath())) {
resources.add( resources.add(createEndpointLinksResource(endpointMapping.getPath(),
createEndpointLinksResource(endpointMapping.getPath(), webEndpoints)); webEndpoints, endpointMediaTypes));
} }
return resources; return resources;
} }
...@@ -102,10 +104,14 @@ public class JerseyEndpointResourceFactory { ...@@ -102,10 +104,14 @@ public class JerseyEndpointResourceFactory {
} }
private Resource createEndpointLinksResource(String endpointPath, private Resource createEndpointLinksResource(String endpointPath,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints) { Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
EndpointMediaTypes endpointMediaTypes) {
Builder resourceBuilder = Resource.builder().path(endpointPath); Builder resourceBuilder = Resource.builder().path(endpointPath);
resourceBuilder.addMethod("GET").produces(MediaType.APPLICATION_JSON).handledBy( resourceBuilder.addMethod("GET")
new EndpointLinksInflector(webEndpoints, this.endpointLinksResolver)); .produces(endpointMediaTypes.getProduced()
.toArray(new String[endpointMediaTypes.getProduced().size()]))
.handledBy(new EndpointLinksInflector(webEndpoints,
this.endpointLinksResolver));
return resourceBuilder.build(); return resourceBuilder.build();
} }
......
...@@ -34,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.OperationType; ...@@ -34,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.ParameterMappingException; import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException; import org.springframework.boot.actuate.endpoint.ParametersMissingException;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
...@@ -87,6 +88,8 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp ...@@ -87,6 +88,8 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints; private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints;
private final EndpointMediaTypes endpointMediaTypes;
private final CorsConfiguration corsConfiguration; private final CorsConfiguration corsConfiguration;
/** /**
...@@ -94,10 +97,12 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp ...@@ -94,10 +97,12 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints * @param collection the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/ */
public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) { Collection<EndpointInfo<WebEndpointOperation>> collection,
this(endpointMapping, collection, null); EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
} }
/** /**
...@@ -105,13 +110,15 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp ...@@ -105,13 +110,15 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the path beneath which all endpoints should be mapped * @param endpointMapping the path beneath which all endpoints should be mapped
* @param webEndpoints the web endpoints * @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints * @param corsConfiguration the CORS configuration for the endpoints
*/ */
public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping, public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints, Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) { EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
this.endpointMapping = endpointMapping; this.endpointMapping = endpointMapping;
this.webEndpoints = webEndpoints; this.webEndpoints = webEndpoints;
this.endpointMediaTypes = endpointMediaTypes;
this.corsConfiguration = corsConfiguration; this.corsConfiguration = corsConfiguration;
setOrder(-100); setOrder(-100);
} }
...@@ -127,11 +134,18 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp ...@@ -127,11 +134,18 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
} }
private void registerLinksMapping() { private void registerLinksMapping() {
registerMapping(new RequestMappingInfo( registerMapping(
new PatternsRequestCondition( new RequestMappingInfo(
pathPatternParser.parse(this.endpointMapping.getPath())), new PatternsRequestCondition(
new RequestMethodsRequestCondition(RequestMethod.GET), null, null, null, pathPatternParser.parse(this.endpointMapping.getPath())),
null, null), this, this.links); new RequestMethodsRequestCondition(RequestMethod.GET), null, null,
null,
new ProducesRequestCondition(
this.endpointMediaTypes.getProduced()
.toArray(new String[this.endpointMediaTypes
.getProduced().size()])),
null),
this, this.links);
} }
@Override @Override
......
...@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.endpoint.EndpointInfo; import org.springframework.boot.actuate.endpoint.EndpointInfo;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate; import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.endpoint.web.EndpointMapping;
...@@ -56,6 +57,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -56,6 +57,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints; private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints;
private final EndpointMediaTypes endpointMediaTypes;
private final CorsConfiguration corsConfiguration; private final CorsConfiguration corsConfiguration;
/** /**
...@@ -63,10 +66,12 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -63,10 +66,12 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints operations * @param collection the web endpoints operations
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/ */
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) { Collection<EndpointInfo<WebEndpointOperation>> collection,
this(endpointMapping, collection, null); EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
} }
/** /**
...@@ -74,13 +79,15 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -74,13 +79,15 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints * @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints * @param corsConfiguration the CORS configuration for the endpoints
*/ */
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints, Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) { EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
this.endpointMapping = endpointMapping; this.endpointMapping = endpointMapping;
this.webEndpoints = webEndpoints; this.webEndpoints = webEndpoints;
this.endpointMediaTypes = endpointMediaTypes;
this.corsConfiguration = corsConfiguration; this.corsConfiguration = corsConfiguration;
setOrder(-100); setOrder(-100);
} }
...@@ -107,8 +114,11 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -107,8 +114,11 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
PatternsRequestCondition patterns = patternsRequestConditionForPattern(""); PatternsRequestCondition patterns = patternsRequestConditionForPattern("");
RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition( RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
RequestMethod.GET); RequestMethod.GET);
ProducesRequestCondition produces = new ProducesRequestCondition(
this.endpointMediaTypes.getProduced().toArray(
new String[this.endpointMediaTypes.getProduced().size()]));
RequestMappingInfo mapping = new RequestMappingInfo(patterns, methods, null, null, RequestMappingInfo mapping = new RequestMappingInfo(patterns, methods, null, null,
null, null, null); null, produces, null);
registerMapping(mapping, this, getLinks()); registerMapping(mapping, this, getLinks());
} }
......
...@@ -30,6 +30,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker; ...@@ -30,6 +30,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException; import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException; import org.springframework.boot.actuate.endpoint.ParametersMissingException;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.Link; import org.springframework.boot.actuate.endpoint.web.Link;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
...@@ -65,10 +66,12 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM ...@@ -65,10 +66,12 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints operations * @param collection the web endpoints operations
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/ */
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) { Collection<EndpointInfo<WebEndpointOperation>> collection,
this(endpointMapping, collection, null); EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
} }
/** /**
...@@ -76,12 +79,13 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM ...@@ -76,12 +79,13 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints * @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints * @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints * @param corsConfiguration the CORS configuration for the endpoints
*/ */
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping, public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints, Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) { EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
super(endpointMapping, webEndpoints, corsConfiguration); super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration);
setOrder(-100); setOrder(-100);
} }
......
...@@ -247,12 +247,14 @@ public class WebAnnotationEndpointDiscovererTests { ...@@ -247,12 +247,14 @@ public class WebAnnotationEndpointDiscovererTests {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configuration); configuration);
try { try {
consumer.accept(new WebAnnotationEndpointDiscoverer(context, consumer.accept(
new ConversionServiceOperationParameterMapper( new WebAnnotationEndpointDiscoverer(context,
DefaultConversionService.getSharedInstance()), new ConversionServiceOperationParameterMapper(
cachingConfigurationFactory, DefaultConversionService.getSharedInstance()),
Collections.singletonList("application/json"), cachingConfigurationFactory,
Collections.singletonList("application/json"))); new EndpointMediaTypes(
Collections.singletonList("application/json"),
Collections.singletonList("application/json"))));
} }
finally { finally {
context.close(); context.close();
......
...@@ -28,6 +28,7 @@ import org.glassfish.jersey.server.model.Resource; ...@@ -28,6 +28,7 @@ import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.servlet.ServletContainer;
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
...@@ -80,12 +81,13 @@ public class JerseyWebEndpointIntegrationTests extends ...@@ -80,12 +81,13 @@ public class JerseyWebEndpointIntegrationTests extends
@Bean @Bean
public ResourceConfig resourceConfig(Environment environment, public ResourceConfig resourceConfig(Environment environment,
WebAnnotationEndpointDiscoverer endpointDiscoverer) { WebAnnotationEndpointDiscoverer endpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
ResourceConfig resourceConfig = new ResourceConfig(); ResourceConfig resourceConfig = new ResourceConfig();
Collection<Resource> resources = new JerseyEndpointResourceFactory() Collection<Resource> resources = new JerseyEndpointResourceFactory()
.createEndpointResources( .createEndpointResources(
new EndpointMapping(environment.getProperty("endpointPath")), new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.discoverEndpoints()); endpointDiscoverer.discoverEndpoints(), endpointMediaTypes);
resourceConfig.registerResources(new HashSet<>(resources)); resourceConfig.registerResources(new HashSet<>(resources));
resourceConfig.register(JacksonFeature.class); resourceConfig.register(JacksonFeature.class);
resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()), resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()),
......
...@@ -21,6 +21,7 @@ import java.util.Arrays; ...@@ -21,6 +21,7 @@ import java.util.Arrays;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
...@@ -110,13 +111,15 @@ public class WebFluxEndpointIntegrationTests ...@@ -110,13 +111,15 @@ public class WebFluxEndpointIntegrationTests
@Bean @Bean
public WebFluxEndpointHandlerMapping webEndpointHandlerMapping( public WebFluxEndpointHandlerMapping webEndpointHandlerMapping(
Environment environment, Environment environment,
WebAnnotationEndpointDiscoverer endpointDiscoverer) { WebAnnotationEndpointDiscoverer endpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
CorsConfiguration corsConfiguration = new CorsConfiguration(); CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com")); corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new WebFluxEndpointHandlerMapping( return new WebFluxEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")), new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.discoverEndpoints(), corsConfiguration); endpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
corsConfiguration);
} }
@Bean @Bean
......
...@@ -21,6 +21,7 @@ import java.util.Arrays; ...@@ -21,6 +21,7 @@ import java.util.Arrays;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests; import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.web.EndpointMapping; import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
...@@ -104,13 +105,15 @@ public class MvcWebEndpointIntegrationTests extends ...@@ -104,13 +105,15 @@ public class MvcWebEndpointIntegrationTests extends
@Bean @Bean
public WebMvcEndpointHandlerMapping webEndpointHandlerMapping( public WebMvcEndpointHandlerMapping webEndpointHandlerMapping(
Environment environment, Environment environment,
WebAnnotationEndpointDiscoverer webEndpointDiscoverer) { WebAnnotationEndpointDiscoverer webEndpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
CorsConfiguration corsConfiguration = new CorsConfiguration(); CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com")); corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new WebMvcEndpointHandlerMapping( return new WebMvcEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")), new EndpointMapping(environment.getProperty("endpointPath")),
webEndpointDiscoverer.discoverEndpoints(), corsConfiguration); webEndpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
corsConfiguration);
} }
} }
......
...@@ -21,6 +21,8 @@ import java.util.Collection; ...@@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import javax.ws.rs.core.MediaType;
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;
import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.BlockJUnit4ClassRunner;
...@@ -28,6 +30,7 @@ import org.junit.runners.model.InitializationError; ...@@ -28,6 +30,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory; import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
...@@ -41,7 +44,6 @@ import org.springframework.context.ApplicationContext; ...@@ -41,7 +44,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
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.http.MediaType;
/** /**
* {@link BlockJUnit4ClassRunner} for Jersey. * {@link BlockJUnit4ClassRunner} for Jersey.
...@@ -90,15 +92,17 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner { ...@@ -90,15 +92,17 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner {
} }
private void customize(ResourceConfig config) { private void customize(ResourceConfig config) {
List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE, List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON,
ActuatorMediaType.V2_JSON); ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext, this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null, new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes); endpointMediaTypes);
Collection<Resource> resources = new JerseyEndpointResourceFactory() Collection<Resource> resources = new JerseyEndpointResourceFactory()
.createEndpointResources(new EndpointMapping("/application"), .createEndpointResources(new EndpointMapping("/application"),
discoverer.discoverEndpoints()); discoverer.discoverEndpoints(), endpointMediaTypes);
config.registerResources(new HashSet<>(resources)); config.registerResources(new HashSet<>(resources));
} }
......
...@@ -24,6 +24,7 @@ import org.junit.runners.model.InitializationError; ...@@ -24,6 +24,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
...@@ -99,12 +100,15 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner { ...@@ -99,12 +100,15 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner {
public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping() { public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping() {
List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE, List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE,
ActuatorMediaType.V2_JSON); ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext, this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null, new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes); endpointMediaTypes);
return new WebFluxEndpointHandlerMapping(new EndpointMapping("/application"), return new WebFluxEndpointHandlerMapping(new EndpointMapping("/application"),
discoverer.discoverEndpoints(), new CorsConfiguration()); discoverer.discoverEndpoints(), endpointMediaTypes,
new CorsConfiguration());
} }
} }
......
...@@ -24,6 +24,7 @@ import org.junit.runners.model.InitializationError; ...@@ -24,6 +24,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper; import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer; import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
...@@ -82,12 +83,15 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner { ...@@ -82,12 +83,15 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner {
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() { public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() {
List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE, List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE,
ActuatorMediaType.V2_JSON); ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer( WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext, this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null, new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes); endpointMediaTypes);
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/application"), return new WebMvcEndpointHandlerMapping(new EndpointMapping("/application"),
discoverer.discoverEndpoints(), new CorsConfiguration()); discoverer.discoverEndpoints(), endpointMediaTypes,
new CorsConfiguration());
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment