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;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
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.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -67,11 +68,12 @@ public class CloudFoundryActuatorAutoConfiguration {
@Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
EndpointProvider<WebEndpointOperation> provider, Environment environment,
EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes, Environment environment,
RestTemplateBuilder builder) {
return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cloudfoundryapplication"),
provider.getEndpoints(), getCorsConfiguration(),
provider.getEndpoints(), endpointMediaTypes, getCorsConfiguration(),
getSecurityInterceptor(builder, environment));
}
......
......@@ -36,6 +36,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException;
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.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
......@@ -76,9 +77,9 @@ class CloudFoundryWebEndpointServletHandlerMapping
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration,
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration,
CloudFoundrySecurityInterceptor securityInterceptor) {
super(endpointMapping, webEndpoints, corsConfiguration);
super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration);
this.securityInterceptor = securityInterceptor;
}
......
......@@ -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.convert.ConversionServiceOperationParameterMapper;
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.annotation.WebAnnotationEndpointDiscoverer;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -71,6 +72,11 @@ public class EndpointAutoConfiguration {
this.applicationContext = applicationContext;
}
@Bean
public EndpointMediaTypes endpointMediaTypes() {
return new EndpointMediaTypes(MEDIA_TYPES, MEDIA_TYPES);
}
@Bean
public EndpointProvider<WebEndpointOperation> webEndpointProvider(
OperationParameterMapper parameterMapper,
......@@ -78,7 +84,7 @@ public class EndpointAutoConfiguration {
Environment environment = this.applicationContext.getEnvironment();
WebAnnotationEndpointDiscoverer endpointDiscoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext, parameterMapper, cachingConfigurationFactory,
MEDIA_TYPES, MEDIA_TYPES);
endpointMediaTypes());
return new EndpointProvider<>(environment, endpointDiscoverer,
EndpointExposure.WEB);
}
......
......@@ -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.web.ManagementContextConfiguration;
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.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
......@@ -55,11 +56,12 @@ class JerseyWebEndpointManagementContextConfiguration {
@Bean
public ResourceConfigCustomizer webEndpointRegistrar(
EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
WebEndpointProperties webEndpointProperties) {
return (resourceConfig) -> resourceConfig.registerResources(
new HashSet<>(new JerseyEndpointResourceFactory().createEndpointResources(
new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints())));
provider.getEndpoints(), endpointMediaTypes)));
}
@Bean
......
......@@ -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.web.ManagementContextConfiguration;
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.reactive.WebFluxEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
......@@ -45,10 +46,11 @@ public class WebFluxEndpointManagementContextConfiguration {
@ConditionalOnMissingBean
public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(
EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
WebEndpointProperties webEndpointProperties) {
return new WebFluxEndpointHandlerMapping(
new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints());
provider.getEndpoints(), endpointMediaTypes);
}
@Bean
......
......@@ -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.server.ManagementServerProperties;
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.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
......@@ -56,11 +57,12 @@ public class WebMvcEndpointManagementContextConfiguration {
@ConditionalOnMissingBean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
EndpointProvider<WebEndpointOperation> provider,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
WebMvcEndpointHandlerMapping handlerMapping = new WebMvcEndpointHandlerMapping(
new EndpointMapping(webEndpointProperties.getBasePath()),
provider.getEndpoints(), getCorsConfiguration(corsProperties));
provider.getEndpoints(), endpointMediaTypes, getCorsConfiguration(corsProperties));
return handlerMapping;
}
......
......@@ -25,6 +25,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
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.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
......@@ -40,11 +41,15 @@ import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
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.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
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}.
......@@ -92,6 +97,18 @@ public class CloudFoundryActuatorAutoConfigurationTests {
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
public void cloudFoundryPlatformActiveSetsApplicationId() throws Exception {
CloudFoundryWebEndpointServletHandlerMapping handlerMapping = getHandlerMapping();
......
......@@ -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.cache.CachingConfiguration;
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.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
......@@ -190,28 +191,35 @@ public class CloudFoundryMvcWebEndpointIntegrationTests {
"app-id");
}
@Bean
public EndpointMediaTypes EndpointMediaTypes() {
return new EndpointMediaTypes(Collections.singletonList("application/json"),
Collections.singletonList("application/json"));
}
@Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
WebAnnotationEndpointDiscoverer webEndpointDiscoverer,
EndpointMediaTypes endpointMediaTypes,
CloudFoundrySecurityInterceptor interceptor) {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cfApplication"),
webEndpointDiscoverer.discoverEndpoints(), corsConfiguration,
interceptor);
webEndpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
corsConfiguration, interceptor);
}
@Bean
public WebAnnotationEndpointDiscoverer webEndpointDiscoverer(
ApplicationContext applicationContext) {
ApplicationContext applicationContext,
EndpointMediaTypes endpointMediaTypes) {
OperationParameterMapper parameterMapper = new ConversionServiceOperationParameterMapper(
DefaultConversionService.getSharedInstance());
return new WebAnnotationEndpointDiscoverer(applicationContext,
parameterMapper, (id) -> new CachingConfiguration(0),
Collections.singletonList("application/json"),
Collections.singletonList("application/json"));
endpointMediaTypes);
}
@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 @@
package org.springframework.boot.actuate.autoconfigure.web.servlet;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
......@@ -24,6 +25,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.endpoint.DefaultEnablement;
import org.springframework.boot.actuate.endpoint.EndpointInfo;
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.WebEndpointHttpMethod;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
......@@ -142,7 +144,9 @@ public class RequestMappingEndpointTests {
WebMvcEndpointHandlerMapping mapping = new WebMvcEndpointHandlerMapping(
new EndpointMapping("application"),
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.afterPropertiesSet();
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;
import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration;
import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory;
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.WebEndpointHttpMethod;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
......@@ -68,17 +69,16 @@ public class WebAnnotationEndpointDiscoverer extends
* @param operationParameterMapper the {@link OperationParameterMapper} used to
* convert arguments when an operation is invoked
* @param cachingConfigurationFactory the {@link CachingConfiguration} factory to use
* @param consumedMediaTypes the media types consumed by web endpoint operations
* @param producedMediaTypes the media types produced by web endpoint operations
* @param endpointMediaTypes the media types produced and consumed by web endpoint
* operations
*/
public WebAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
OperationParameterMapper operationParameterMapper,
CachingConfigurationFactory cachingConfigurationFactory,
Collection<String> consumedMediaTypes,
Collection<String> producedMediaTypes) {
EndpointMediaTypes endpointMediaTypes) {
super(applicationContext,
new WebEndpointOperationFactory(operationParameterMapper,
consumedMediaTypes, producedMediaTypes),
endpointMediaTypes),
WebEndpointOperation::getRequestPredicate, cachingConfigurationFactory);
}
......@@ -119,16 +119,12 @@ public class WebAnnotationEndpointDiscoverer extends
private final OperationParameterMapper parameterMapper;
private final Collection<String> consumedMediaTypes;
private final Collection<String> producedMediaTypes;
private final EndpointMediaTypes endpointMediaTypes;
private WebEndpointOperationFactory(OperationParameterMapper parameterMapper,
Collection<String> consumedMediaTypes,
Collection<String> producedMediaTypes) {
EndpointMediaTypes endpointMediaTypes) {
this.parameterMapper = parameterMapper;
this.consumedMediaTypes = consumedMediaTypes;
this.producedMediaTypes = producedMediaTypes;
this.endpointMediaTypes = endpointMediaTypes;
}
@Override
......@@ -172,7 +168,7 @@ public class WebAnnotationEndpointDiscoverer extends
private Collection<String> determineConsumedMediaTypes(
WebEndpointHttpMethod httpMethod, Method method) {
if (WebEndpointHttpMethod.POST == httpMethod && consumesRequestBody(method)) {
return this.consumedMediaTypes;
return this.endpointMediaTypes.getConsumed();
}
return Collections.emptyList();
}
......@@ -189,7 +185,7 @@ public class WebAnnotationEndpointDiscoverer extends
if (producesResourceResponseBody(method)) {
return Collections.singletonList("application/octet-stream");
}
return this.producedMediaTypes;
return this.endpointMediaTypes.getProduced();
}
private boolean producesResourceResponseBody(Method method) {
......
......@@ -27,7 +27,6 @@ import java.util.function.Function;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
......@@ -43,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException;
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.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
......@@ -68,18 +68,20 @@ public class JerseyEndpointResourceFactory {
* {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @return the resources for the operations
*/
public Collection<Resource> createEndpointResources(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints) {
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
EndpointMediaTypes endpointMediaTypes) {
List<Resource> resources = new ArrayList<>();
webEndpoints.stream()
.flatMap((endpointInfo) -> endpointInfo.getOperations().stream())
.map((operation) -> createResource(endpointMapping, operation))
.forEach(resources::add);
if (StringUtils.hasText(endpointMapping.getPath())) {
resources.add(
createEndpointLinksResource(endpointMapping.getPath(), webEndpoints));
resources.add(createEndpointLinksResource(endpointMapping.getPath(),
webEndpoints, endpointMediaTypes));
}
return resources;
}
......@@ -102,10 +104,14 @@ public class JerseyEndpointResourceFactory {
}
private Resource createEndpointLinksResource(String endpointPath,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints) {
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
EndpointMediaTypes endpointMediaTypes) {
Builder resourceBuilder = Resource.builder().path(endpointPath);
resourceBuilder.addMethod("GET").produces(MediaType.APPLICATION_JSON).handledBy(
new EndpointLinksInflector(webEndpoints, this.endpointLinksResolver));
resourceBuilder.addMethod("GET")
.produces(endpointMediaTypes.getProduced()
.toArray(new String[endpointMediaTypes.getProduced().size()]))
.handledBy(new EndpointLinksInflector(webEndpoints,
this.endpointLinksResolver));
return resourceBuilder.build();
}
......
......@@ -34,6 +34,7 @@ import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException;
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.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
......@@ -87,6 +88,8 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints;
private final EndpointMediaTypes endpointMediaTypes;
private final CorsConfiguration corsConfiguration;
/**
......@@ -94,10 +97,12 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
* operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/
public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) {
this(endpointMapping, collection, null);
Collection<EndpointInfo<WebEndpointOperation>> collection,
EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
}
/**
......@@ -105,13 +110,15 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
* operations of the given {@code webEndpoints}.
* @param endpointMapping the path beneath which all endpoints should be mapped
* @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
*/
public WebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) {
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
this.endpointMapping = endpointMapping;
this.webEndpoints = webEndpoints;
this.endpointMediaTypes = endpointMediaTypes;
this.corsConfiguration = corsConfiguration;
setOrder(-100);
}
......@@ -127,11 +134,18 @@ public class WebFluxEndpointHandlerMapping extends RequestMappingInfoHandlerMapp
}
private void registerLinksMapping() {
registerMapping(new RequestMappingInfo(
new PatternsRequestCondition(
pathPatternParser.parse(this.endpointMapping.getPath())),
new RequestMethodsRequestCondition(RequestMethod.GET), null, null, null,
null, null), this, this.links);
registerMapping(
new RequestMappingInfo(
new PatternsRequestCondition(
pathPatternParser.parse(this.endpointMapping.getPath())),
new RequestMethodsRequestCondition(RequestMethod.GET), null, null,
null,
new ProducesRequestCondition(
this.endpointMediaTypes.getProduced()
.toArray(new String[this.endpointMediaTypes
.getProduced().size()])),
null),
this, this.links);
}
@Override
......
......@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.InitializingBean;
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.WebEndpointOperation;
import org.springframework.boot.endpoint.web.EndpointMapping;
......@@ -56,6 +57,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
private final Collection<EndpointInfo<WebEndpointOperation>> webEndpoints;
private final EndpointMediaTypes endpointMediaTypes;
private final CorsConfiguration corsConfiguration;
/**
......@@ -63,10 +66,12 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
* operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints operations
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) {
this(endpointMapping, collection, null);
Collection<EndpointInfo<WebEndpointOperation>> collection,
EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
}
/**
......@@ -74,13 +79,15 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
* operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
*/
public AbstractWebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) {
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
this.endpointMapping = endpointMapping;
this.webEndpoints = webEndpoints;
this.endpointMediaTypes = endpointMediaTypes;
this.corsConfiguration = corsConfiguration;
setOrder(-100);
}
......@@ -107,8 +114,11 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
PatternsRequestCondition patterns = patternsRequestConditionForPattern("");
RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
RequestMethod.GET);
ProducesRequestCondition produces = new ProducesRequestCondition(
this.endpointMediaTypes.getProduced().toArray(
new String[this.endpointMediaTypes.getProduced().size()]));
RequestMappingInfo mapping = new RequestMappingInfo(patterns, methods, null, null,
null, null, null);
null, produces, null);
registerMapping(mapping, this, getLinks());
}
......
......@@ -30,6 +30,7 @@ import org.springframework.boot.actuate.endpoint.OperationInvoker;
import org.springframework.boot.actuate.endpoint.ParameterMappingException;
import org.springframework.boot.actuate.endpoint.ParametersMissingException;
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.WebEndpointOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
......@@ -65,10 +66,12 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
* operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param collection the web endpoints operations
* @param endpointMediaTypes media types consumed and produced by the endpoints
*/
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> collection) {
this(endpointMapping, collection, null);
Collection<EndpointInfo<WebEndpointOperation>> collection,
EndpointMediaTypes endpointMediaTypes) {
this(endpointMapping, collection, endpointMediaTypes, null);
}
/**
......@@ -76,12 +79,13 @@ public class WebMvcEndpointHandlerMapping extends AbstractWebMvcEndpointHandlerM
* operations of the given {@code webEndpoints}.
* @param endpointMapping the base mapping for all endpoints
* @param webEndpoints the web endpoints
* @param endpointMediaTypes media types consumed and produced by the endpoints
* @param corsConfiguration the CORS configuration for the endpoints
*/
public WebMvcEndpointHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration) {
super(endpointMapping, webEndpoints, corsConfiguration);
EndpointMediaTypes endpointMediaTypes, CorsConfiguration corsConfiguration) {
super(endpointMapping, webEndpoints, endpointMediaTypes, corsConfiguration);
setOrder(-100);
}
......
......@@ -247,12 +247,14 @@ public class WebAnnotationEndpointDiscovererTests {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configuration);
try {
consumer.accept(new WebAnnotationEndpointDiscoverer(context,
new ConversionServiceOperationParameterMapper(
DefaultConversionService.getSharedInstance()),
cachingConfigurationFactory,
Collections.singletonList("application/json"),
Collections.singletonList("application/json")));
consumer.accept(
new WebAnnotationEndpointDiscoverer(context,
new ConversionServiceOperationParameterMapper(
DefaultConversionService.getSharedInstance()),
cachingConfigurationFactory,
new EndpointMediaTypes(
Collections.singletonList("application/json"),
Collections.singletonList("application/json"))));
}
finally {
context.close();
......
......@@ -28,6 +28,7 @@ import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.servlet.ServletContainer;
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.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
......@@ -80,12 +81,13 @@ public class JerseyWebEndpointIntegrationTests extends
@Bean
public ResourceConfig resourceConfig(Environment environment,
WebAnnotationEndpointDiscoverer endpointDiscoverer) {
WebAnnotationEndpointDiscoverer endpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
ResourceConfig resourceConfig = new ResourceConfig();
Collection<Resource> resources = new JerseyEndpointResourceFactory()
.createEndpointResources(
new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.discoverEndpoints());
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes);
resourceConfig.registerResources(new HashSet<>(resources));
resourceConfig.register(JacksonFeature.class);
resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()),
......
......@@ -21,6 +21,7 @@ import java.util.Arrays;
import org.junit.Test;
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.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
......@@ -110,13 +111,15 @@ public class WebFluxEndpointIntegrationTests
@Bean
public WebFluxEndpointHandlerMapping webEndpointHandlerMapping(
Environment environment,
WebAnnotationEndpointDiscoverer endpointDiscoverer) {
WebAnnotationEndpointDiscoverer endpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new WebFluxEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")),
endpointDiscoverer.discoverEndpoints(), corsConfiguration);
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
corsConfiguration);
}
@Bean
......
......@@ -21,6 +21,7 @@ import java.util.Arrays;
import org.junit.Test;
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.endpoint.web.EndpointMapping;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
......@@ -104,13 +105,15 @@ public class MvcWebEndpointIntegrationTests extends
@Bean
public WebMvcEndpointHandlerMapping webEndpointHandlerMapping(
Environment environment,
WebAnnotationEndpointDiscoverer webEndpointDiscoverer) {
WebAnnotationEndpointDiscoverer webEndpointDiscoverer,
EndpointMediaTypes endpointMediaTypes) {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
return new WebMvcEndpointHandlerMapping(
new EndpointMapping(environment.getProperty("endpointPath")),
webEndpointDiscoverer.discoverEndpoints(), corsConfiguration);
webEndpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
corsConfiguration);
}
}
......
......@@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import javax.ws.rs.core.MediaType;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.junit.runners.BlockJUnit4ClassRunner;
......@@ -28,6 +30,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
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.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
......@@ -41,7 +44,6 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
/**
* {@link BlockJUnit4ClassRunner} for Jersey.
......@@ -90,15 +92,17 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner {
}
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);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes);
endpointMediaTypes);
Collection<Resource> resources = new JerseyEndpointResourceFactory()
.createEndpointResources(new EndpointMapping("/application"),
discoverer.discoverEndpoints());
discoverer.discoverEndpoints(), endpointMediaTypes);
config.registerResources(new HashSet<>(resources));
}
......
......@@ -24,6 +24,7 @@ import org.junit.runners.model.InitializationError;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
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.reactive.WebFluxEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
......@@ -99,12 +100,15 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner {
public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping() {
List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE,
ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes);
endpointMediaTypes);
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;
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceOperationParameterMapper;
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.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
......@@ -82,12 +83,15 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner {
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() {
List<String> mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE,
ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
mediaTypes);
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
this.applicationContext,
new ConversionServiceOperationParameterMapper(), (id) -> null,
mediaTypes, mediaTypes);
endpointMediaTypes);
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