diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java index d04c739c5d6..d22961a5f62 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java @@ -18,12 +18,15 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; +import javax.annotation.PostConstruct; + import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.model.Resource; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; @@ -32,6 +35,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; import org.springframework.boot.actuate.endpoint.web.EndpointMapping; import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.ExposableServletEndpoint; import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint; import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier; @@ -64,22 +68,15 @@ import org.springframework.util.StringUtils; class JerseyWebEndpointManagementContextConfiguration { @Bean - ResourceConfigCustomizer webEndpointRegistrar(WebEndpointsSupplier webEndpointsSupplier, + JerseyWebEndpointsResourcesRegistrar jerseyWebEndpointsResourcesRegistrar(Environment environment, + ObjectProvider resourceConfig, WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, - WebEndpointProperties webEndpointProperties, Environment environment) { - List> allEndpoints = new ArrayList<>(); - allEndpoints.addAll(webEndpointsSupplier.getEndpoints()); - allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); - return (resourceConfig) -> { - JerseyEndpointResourceFactory resourceFactory = new JerseyEndpointResourceFactory(); - String basePath = webEndpointProperties.getBasePath(); - EndpointMapping endpointMapping = new EndpointMapping(basePath); - Collection webEndpoints = Collections - .unmodifiableCollection(webEndpointsSupplier.getEndpoints()); - resourceConfig.registerResources(new HashSet<>(resourceFactory.createEndpointResources(endpointMapping, - webEndpoints, endpointMediaTypes, new EndpointLinksResolver(allEndpoints, basePath), - shouldRegisterLinksMapping(environment, basePath)))); - }; + WebEndpointProperties webEndpointProperties) { + String basePath = webEndpointProperties.getBasePath(); + boolean shouldRegisterLinks = shouldRegisterLinksMapping(environment, basePath); + shouldRegisterLinksMapping(environment, basePath); + return new JerseyWebEndpointsResourcesRegistrar(resourceConfig.getIfAvailable(), webEndpointsSupplier, + servletEndpointsSupplier, endpointMediaTypes, basePath, shouldRegisterLinks); } private boolean shouldRegisterLinksMapping(Environment environment, String basePath) { @@ -87,4 +84,65 @@ class JerseyWebEndpointManagementContextConfiguration { || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT); } + /** + * Register endpoints with the {@link ResourceConfig}. The + * {@link ResourceConfigCustomizer} cannot be used because we don't want to apply + */ + static class JerseyWebEndpointsResourcesRegistrar { + + private final ResourceConfig resourceConfig; + + private final WebEndpointsSupplier webEndpointsSupplier; + + private final ServletEndpointsSupplier servletEndpointsSupplier; + + private final EndpointMediaTypes mediaTypes; + + private final String basePath; + + private final boolean shouldRegisterLinks; + + JerseyWebEndpointsResourcesRegistrar(ResourceConfig resourceConfig, WebEndpointsSupplier webEndpointsSupplier, + ServletEndpointsSupplier servletEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, + String basePath, boolean shouldRegisterLinks) { + super(); + this.resourceConfig = resourceConfig; + this.webEndpointsSupplier = webEndpointsSupplier; + this.servletEndpointsSupplier = servletEndpointsSupplier; + this.mediaTypes = endpointMediaTypes; + this.basePath = basePath; + this.shouldRegisterLinks = shouldRegisterLinks; + } + + @PostConstruct + void register() { + // We can't easily use @ConditionalOnBean because @AutoConfigureBefore is + // not an option for management contexts. Instead we manually check if + // the resource config bean exists + if (this.resourceConfig == null) { + return; + } + Collection webEndpoints = this.webEndpointsSupplier.getEndpoints(); + Collection servletEndpoints = this.servletEndpointsSupplier.getEndpoints(); + EndpointLinksResolver linksResolver = getLinksResolver(webEndpoints, servletEndpoints); + EndpointMapping mapping = new EndpointMapping(this.basePath); + JerseyEndpointResourceFactory resourceFactory = new JerseyEndpointResourceFactory(); + register(resourceFactory.createEndpointResources(mapping, webEndpoints, this.mediaTypes, linksResolver, + this.shouldRegisterLinks)); + } + + private EndpointLinksResolver getLinksResolver(Collection webEndpoints, + Collection servletEndpoints) { + List> endpoints = new ArrayList<>(webEndpoints.size() + servletEndpoints.size()); + endpoints.addAll(webEndpoints); + endpoints.addAll(servletEndpoints); + return new EndpointLinksResolver(endpoints, this.basePath); + } + + private void register(Collection resources) { + this.resourceConfig.registerResources(new HashSet<>(resources)); + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyManagementContextConfiguration.java index dc409df6084..ed9117dda68 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyManagementContextConfiguration.java @@ -18,8 +18,6 @@ package org.springframework.boot.actuate.autoconfigure.web.jersey; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -41,10 +39,8 @@ class JerseyManagementContextConfiguration { } @Bean - ResourceConfig resourceConfig(ObjectProvider resourceConfigCustomizers) { - ResourceConfig resourceConfig = new ResourceConfig(); - resourceConfigCustomizers.orderedStream().forEach((customizer) -> customizer.customize(resourceConfig)); - return resourceConfig; + ResourceConfig resourceConfig() { + return new ResourceConfig(); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfiguration.java index 83e88a32a7e..5a2f1edfb3f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfiguration.java @@ -38,9 +38,9 @@ import org.springframework.context.annotation.Import; * @since 2.1.0 */ @ManagementContextConfiguration(ManagementContextType.SAME) -@ConditionalOnMissingBean(ResourceConfig.class) @Import(JerseyManagementContextConfiguration.class) @EnableConfigurationProperties(JerseyProperties.class) +@ConditionalOnMissingBean(ResourceConfig.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnClass(ResourceConfig.class) @ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfigurationTests.java index 014a9cc6ccc..5e161080ed5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfigurationTests.java @@ -22,10 +22,10 @@ import org.glassfish.jersey.server.ResourceConfig; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration.JerseyWebEndpointsResourcesRegistrar; import org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration; import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; @@ -46,8 +46,8 @@ class JerseyWebEndpointManagementContextConfigurationTests { .withBean(WebEndpointsSupplier.class, () -> Collections::emptyList); @Test - void resourceConfigCustomizerForEndpointsIsAutoConfigured() { - this.runner.run((context) -> assertThat(context).hasSingleBean(ResourceConfigCustomizer.class)); + void jerseyWebEndpointsResourcesRegistrarForEndpointsIsAutoConfigured() { + this.runner.run((context) -> assertThat(context).hasSingleBean(JerseyWebEndpointsResourcesRegistrar.class)); } @Test diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyChildManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyChildManagementContextConfigurationTests.java index 4d34a931588..9d57fa5f6ed 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyChildManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseyChildManagementContextConfigurationTests.java @@ -21,19 +21,14 @@ import org.glassfish.jersey.servlet.ServletContainer; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; /** * Tests for {@link JerseyChildManagementContextConfiguration}. @@ -61,16 +56,6 @@ class JerseyChildManagementContextConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class)); } - @Test - void resourceConfigIsCustomizedWithResourceConfigCustomizerBean() { - this.contextRunner.withUserConfiguration(CustomizerConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(ResourceConfig.class); - ResourceConfig config = context.getBean(ResourceConfig.class); - ResourceConfigCustomizer customizer = context.getBean(ResourceConfigCustomizer.class); - verify(customizer).customize(config); - }); - } - @Test void jerseyApplicationPathIsAutoConfigured() { this.contextRunner.run((context) -> { @@ -93,14 +78,4 @@ class JerseyChildManagementContextConfigurationTests { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ResourceConfig.class)); } - @Configuration(proxyBeanMethods = false) - static class CustomizerConfiguration { - - @Bean - ResourceConfigCustomizer resourceConfigCustomizer() { - return mock(ResourceConfigCustomizer.class); - } - - } - } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfigurationTests.java index 235efb96a21..d29c5e8d739 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfigurationTests.java @@ -20,7 +20,6 @@ import org.glassfish.jersey.servlet.ServletContainer; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath; import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath; import org.springframework.boot.test.context.FilteredClassLoader; @@ -33,7 +32,6 @@ import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; /** * Tests for {@link JerseySameManagementContextConfiguration}. @@ -60,16 +58,6 @@ class JerseySameManagementContextConfigurationTests { .run((context) -> assertThat(context).doesNotHaveBean(JerseySameManagementContextConfiguration.class)); } - @Test - void resourceConfigIsCustomizedWithResourceConfigCustomizerBean() { - this.contextRunner.withUserConfiguration(CustomizerConfiguration.class).run((context) -> { - assertThat(context).hasSingleBean(ResourceConfig.class); - ResourceConfig config = context.getBean(ResourceConfig.class); - ResourceConfigCustomizer customizer = context.getBean(ResourceConfigCustomizer.class); - verify(customizer).customize(config); - }); - } - @Test void jerseyApplicationPathIsAutoConfiguredWhenNeeded() { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DefaultJerseyApplicationPath.class)); @@ -122,14 +110,4 @@ class JerseySameManagementContextConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class CustomizerConfiguration { - - @Bean - ResourceConfigCustomizer resourceConfigCustomizer() { - return mock(ResourceConfigCustomizer.class); - } - - } - } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyManagementPortTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyManagementPortTests.java new file mode 100644 index 00000000000..05c0b6b9bd2 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyManagementPortTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sample.jersey; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports. + * + * @author Madhura Bhave + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.server.port=0") +public class JerseyManagementPortTests { + + @LocalServerPort + private int port; + + @LocalManagementPort + private int managementPort; + + @Autowired + private TestRestTemplate testRestTemplate; + + @Test + public void resourceShouldBeAvailableOnMainPort() { + ResponseEntity entity = this.testRestTemplate.getForEntity("http://localhost:" + this.port + "/test", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("test"); + } + + @Test + public void resourceShouldNotBeAvailableOnManagementPort() { + ResponseEntity entity = this.testRestTemplate + .getForEntity("http://localhost:" + this.managementPort + "/test", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @TestConfiguration + static class ResourceConfigConfiguration { + + @Bean + public ResourceConfigCustomizer customizer() { + return new ResourceConfigCustomizer() { + @Override + public void customize(ResourceConfig config) { + config.register(TestEndpoint.class); + } + }; + } + + @Path("/test") + public static class TestEndpoint { + + @GET + public String test() { + return "test"; + } + + } + + } + +}