Expose Jolokia directly rather than via an endpoint

Jolokia is a 100% web concern and does not fit in the Endpoint
infrastructure. This commit removes `JolokiaMvcEndpoint` and exposes
the servlet directly instead while still being part of the
management context. As such, the Jolokia servlet is exposed beneath
the management context path and will move to a separate port when
the management port is not the same as the main server port.

Closes gh-9843
This commit is contained in:
Stephane Nicoll 2017-07-28 20:36:24 +02:00 committed by Andy Wilkinson
parent 2eb3da5b4d
commit 7e97495cdf
14 changed files with 338 additions and 716 deletions

View File

@ -1,119 +0,0 @@
/*
* 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.jolokia;
import java.util.Properties;
import org.jolokia.http.AgentServlet;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration.JolokiaCondition;
import org.springframework.boot.actuate.autoconfigure.security.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.web.servlet.mvc.ServletWrappingController;
/**
* {@link EnableAutoConfiguration Auto-configuration} for embedding Jolokia, a JMX-HTTP
* bridge giving an alternative to JSR-160 connectors.
*
* <p>
* This configuration will get automatically enabled as soon as the Jolokia
* {@link AgentServlet} is on the classpath. To disable it set
* {@code endpoints.jolokia.enabled: false} or {@code endpoints.enabled: false}.
*
* <p>
* Additional configuration parameters for Jolokia can be provided by specifying
* {@code jolokia.config.*} properties. See the
* <a href="http://jolokia.org">http://jolokia.org</a> web site for more information on
* supported configuration parameters.
*
* @author Christian Dupuis
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
* @since 2.0.0
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ AgentServlet.class, ServletWrappingController.class })
@Conditional(JolokiaCondition.class)
@AutoConfigureBefore(ManagementWebSecurityAutoConfiguration.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(JolokiaProperties.class)
public class JolokiaAutoConfiguration {
private final JolokiaProperties properties;
public JolokiaAutoConfiguration(JolokiaProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public JolokiaMvcEndpoint jolokiaEndpoint() {
JolokiaMvcEndpoint endpoint = new JolokiaMvcEndpoint();
endpoint.setInitParameters(getInitParameters());
return endpoint;
}
private Properties getInitParameters() {
Properties initParameters = new Properties();
initParameters.putAll(this.properties.getConfig());
return initParameters;
}
/**
* Condition to check that the Jolokia endpoint is enabled.
*/
static class JolokiaCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
boolean endpointsEnabled = isEnabled(context, "endpoints.", true);
ConditionMessage.Builder message = ConditionMessage.forCondition("Jolokia");
if (isEnabled(context, "endpoints.jolokia.", endpointsEnabled)) {
return ConditionOutcome.match(message.because("enabled"));
}
return ConditionOutcome.noMatch(message.because("not enabled"));
}
private boolean isEnabled(ConditionContext context, String prefix,
boolean defaultValue) {
return context.getEnvironment().getProperty(prefix + "enabled", Boolean.class,
defaultValue);
}
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.jolokia;
import org.jolokia.http.AgentServlet;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.mvc.ServletWrappingController;
/**
* {@link ManagementContextConfiguration} for embedding Jolokia, a JMX-HTTP bridge giving
* an alternative to JSR-160 connectors.
*
* <p>
* This configuration will get automatically enabled as soon as the Jolokia
* {@link AgentServlet} is on the classpath. To disable it set
* {@code management.jolokia.enabled=false}.
*
* <p>
* Additional configuration parameters for Jolokia can be provided by specifying
* {@code management.jolokia.config.*} properties. See the
* <a href="http://jolokia.org">http://jolokia.org</a> web site for more information on
* supported configuration parameters.
*
* @author Christian Dupuis
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
* @author Stephane Nicoll
* @since 2.0.0
*/
@ManagementContextConfiguration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ AgentServlet.class, ServletWrappingController.class })
@ConditionalOnProperty(value = "management.jolokia.enabled", matchIfMissing = true)
@EnableConfigurationProperties(JolokiaProperties.class)
public class JolokiaManagementContextConfiguration {
private final ManagementServletContext managementServletContext;
private final JolokiaProperties properties;
public JolokiaManagementContextConfiguration(
ManagementServletContext managementServletContext,
JolokiaProperties properties) {
this.managementServletContext = managementServletContext;
this.properties = properties;
}
@Bean
public ServletRegistrationBean<AgentServlet> jolokiaServlet() {
String path = this.managementServletContext.getContextPath()
+ this.properties.getPath();
String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*");
ServletRegistrationBean<AgentServlet> registration = new ServletRegistrationBean<>(
new AgentServlet(), urlMapping);
registration.setInitParameters(this.properties.getConfig());
return registration;
}
}

View File

@ -26,23 +26,46 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*
* @author Christian Dupuis
* @author Dave Syer
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "jolokia")
@ConfigurationProperties(prefix = "management.jolokia")
public class JolokiaProperties {
/**
* Enable Jolokia.
*/
private boolean enabled = true;
/**
* Path at which Jolokia will be available.
*/
private String path = "/jolokia";
/**
* Jolokia settings. These are traditionally set using servlet parameters. Refer to
* the documentation of Jolokia for more details.
*/
private Map<String, String> config = new HashMap<>();
private final Map<String, String> config = new HashMap<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public Map<String, String> getConfig() {
return this.config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View File

@ -1,121 +0,0 @@
/*
* 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.mvc;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.jolokia.http.AgentServlet;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.ServletWrappingController;
import org.springframework.web.util.UrlPathHelper;
/**
* {@link MvcEndpoint} to expose Jolokia.
*
* @author Christian Dupuis
* @author Andy Wilkinson
*/
@ConfigurationProperties(prefix = "endpoints.jolokia", ignoreUnknownFields = false)
public class JolokiaMvcEndpoint extends AbstractNamedMvcEndpoint implements
InitializingBean, ApplicationContextAware, ServletContextAware, DisposableBean {
private final ServletWrappingController controller = new ServletWrappingController();
public JolokiaMvcEndpoint() {
super("jolokia", "/jolokia");
this.controller.setServletClass(AgentServlet.class);
this.controller.setServletName("jolokia");
}
@Override
public void afterPropertiesSet() throws Exception {
this.controller.afterPropertiesSet();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.controller.setServletContext(servletContext);
}
public void setInitParameters(Properties initParameters) {
this.controller.setInitParameters(initParameters);
}
@Override
public final void setApplicationContext(ApplicationContext context)
throws BeansException {
this.controller.setApplicationContext(context);
}
@Override
public void destroy() {
this.controller.destroy();
}
@RequestMapping("/**")
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return this.controller.handleRequest(new PathStripper(request, getPath()),
response);
}
private static class PathStripper extends HttpServletRequestWrapper {
private final String path;
private final UrlPathHelper urlPathHelper;
PathStripper(HttpServletRequest request, String path) {
super(request);
this.path = path;
this.urlPathHelper = new UrlPathHelper();
}
@Override
public String getPathInfo() {
String value = this.urlPathHelper.decodeRequestString(
(HttpServletRequest) getRequest(), super.getRequestURI());
if (value.contains(this.path)) {
value = value.substring(value.indexOf(this.path) + this.path.length());
}
int index = value.indexOf("?");
if (index > 0) {
value = value.substring(0, index);
}
while (value.startsWith("/")) {
value = value.substring(1);
}
return value;
}
}
}

View File

@ -6,7 +6,6 @@ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoC
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.security.ManagementWebSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration,\
@ -19,4 +18,5 @@ org.springframework.boot.actuate.autoconfigure.trace.TraceWebFilterAutoConfigura
org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcManagementContextConfiguration
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaManagementContextConfiguration

View File

@ -20,7 +20,6 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
@ -74,8 +73,7 @@ public class SpringApplicationHierarchyTests {
}
@EnableAutoConfiguration(exclude = { JolokiaAutoConfiguration.class,
EndpointMBeanExportAutoConfiguration.class,
@EnableAutoConfiguration(exclude = { EndpointMBeanExportAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,

View File

@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaManagementContextConfiguration;
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint;
import org.springframework.boot.actuate.endpoint.BeansEndpoint;
import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint;
@ -43,7 +43,6 @@ import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
@ -96,7 +95,6 @@ public class MvcEndpointPathConfigurationTests {
new Object[] { "flyway", FlywayEndpoint.class },
new Object[] { "health", HealthMvcEndpoint.class },
new Object[] { "info", InfoEndpoint.class },
new Object[] { "jolokia", JolokiaMvcEndpoint.class },
new Object[] { "liquibase", LiquibaseEndpoint.class },
new Object[] { "logfile", LogFileMvcEndpoint.class },
new Object[] { "loggers", LoggersMvcEndpoint.class },
@ -144,7 +142,8 @@ public class MvcEndpointPathConfigurationTests {
@Configuration
@ImportAutoConfiguration({ EndpointAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, AuditAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class })
EndpointWebMvcAutoConfiguration.class,
JolokiaManagementContextConfiguration.class })
protected static class TestConfiguration {

View File

@ -1,171 +0,0 @@
/*
* 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.jolokia;
import java.util.Collection;
import java.util.Collections;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor;
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.boot.actuate.servlet.MockServletWebServerFactory;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JolokiaAutoConfiguration}.
*
* @author Christian Dupuis
* @author Andy Wilkinson
*/
public class JolokiaAutoConfigurationTests {
private AnnotationConfigServletWebServerApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
if (Config.webServerFactory != null) {
Config.webServerFactory = null;
}
}
@Test
public void agentServletRegisteredWithAppContext() throws Exception {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues
.of("jolokia.config[key1]:value1", "jolokia.config[key2]:value2")
.applyTo(this.context);
this.context.register(Config.class, WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(JolokiaMvcEndpoint.class)).hasSize(1);
}
@Test
public void agentServletWithCustomPath() throws Exception {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of("endpoints.jolokia.path=/foo/bar").applyTo(this.context);
this.context.register(EndpointsConfig.class, WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(JolokiaMvcEndpoint.class)).hasSize(1);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
mockMvc.perform(MockMvcRequestBuilders.get("/foo/bar"))
.andExpect(MockMvcResultMatchers.content()
.string(Matchers.containsString("\"request\":{\"type\"")));
}
@Test
public void endpointDisabled() throws Exception {
assertEndpointDisabled("endpoints.jolokia.enabled:false");
}
@Test
public void allEndpointsDisabled() throws Exception {
assertEndpointDisabled("endpoints.enabled:false");
}
@Test
public void endpointEnabledAsOverride() throws Exception {
assertEndpointEnabled("endpoints.enabled:false",
"endpoints.jolokia.enabled:true");
}
private void assertEndpointDisabled(String... pairs) {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of(pairs).applyTo(this.context);
this.context.register(Config.class, WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(JolokiaMvcEndpoint.class)).isEmpty();
}
private void assertEndpointEnabled(String... pairs) {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of(pairs).applyTo(this.context);
this.context.register(Config.class, WebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JolokiaAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(JolokiaMvcEndpoint.class)).hasSize(1);
}
@Configuration
protected static class EndpointsConfig extends Config {
@Bean
public EndpointHandlerMapping endpointHandlerMapping(
Collection<NamedMvcEndpoint> endpoints) {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints);
mapping.setSecurityInterceptor(new MvcEndpointSecurityInterceptor(false,
Collections.<String>emptyList()));
return mapping;
}
}
@Configuration
@EnableConfigurationProperties
protected static class Config {
protected static MockServletWebServerFactory webServerFactory = null;
@Bean
public ServletWebServerFactory webServerFactory() {
if (webServerFactory == null) {
webServerFactory = new MockServletWebServerFactory();
}
return webServerFactory;
}
@Bean
public WebServerFactoryCustomizerBeanPostProcessor ServletWebServerCustomizerBeanPostProcessor() {
return new WebServerFactoryCustomizerBeanPostProcessor();
}
}
}

View File

@ -0,0 +1,119 @@
/*
* 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.jolokia;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link JolokiaManagementContextConfiguration}.
*
* @author Stephane Nicoll
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
@TestPropertySource(properties = "management.security.enabled=false")
public class JolokiaManagementContextConfigurationIntegrationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void jolokiaIsExposed() {
ResponseEntity<String> response = this.restTemplate
.getForEntity("/application/jolokia", String.class);
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("\"agent\"");
assertThat(response.getBody()).contains("\"request\":{\"type\"");
}
@Test
public void search() {
ResponseEntity<String> response = this.restTemplate
.getForEntity("/application/jolokia/search/java.lang:*", String.class);
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("GarbageCollector");
}
@Test
public void read() {
ResponseEntity<String> response = this.restTemplate.getForEntity(
"/application/jolokia/read/java.lang:type=Memory", String.class);
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("NonHeapMemoryUsage");
}
@Test
public void list() {
ResponseEntity<String> response = this.restTemplate.getForEntity(
"/application/jolokia/list/java.lang/type=Memory/attr", String.class);
assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("NonHeapMemoryUsage");
}
@Configuration
@MinimalWebConfiguration
@Import({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
JolokiaManagementContextConfiguration.class })
protected static class Application {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class,
WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration {
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.jolokia;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link JolokiaManagementContextConfiguration}.
*
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
public class JolokiaManagementContextConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(EndpointWebMvcAutoConfiguration.class,
JolokiaManagementContextConfiguration.class));
@Test
public void jolokiaIsEnabledByDefault() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(ServletRegistrationBean.class);
ServletRegistrationBean<?> registrationBean = context
.getBean(ServletRegistrationBean.class);
assertThat(registrationBean.getUrlMappings())
.contains("/application/jolokia/*");
assertThat(registrationBean.getInitParameters()).isEmpty();
});
}
@Test
public void jolokiaCanBeDisabled() {
this.contextRunner.withPropertyValues("management.jolokia.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(ServletRegistrationBean.class));
}
@Test
public void customPath() {
this.contextRunner.withPropertyValues("management.jolokia.path=/lokia")
.run(isDefinedOnPath("/application/lokia/*"));
}
@Test
public void customManagementPath() {
this.contextRunner.withPropertyValues("management.context-path=/admin")
.run(isDefinedOnPath("/admin/jolokia/*"));
}
@Test
public void customInitParameters() {
this.contextRunner.withPropertyValues("management.jolokia.config.debug=true")
.run((context) -> {
assertThat(context).hasSingleBean(ServletRegistrationBean.class);
ServletRegistrationBean<?> registrationBean = context
.getBean(ServletRegistrationBean.class);
assertThat(registrationBean.getInitParameters())
.containsOnly(entry("debug", "true"));
});
}
private ContextConsumer<AssertableWebApplicationContext> isDefinedOnPath(
String path) {
return (context) -> {
assertThat(context).hasSingleBean(ServletRegistrationBean.class);
ServletRegistrationBean<?> registrationBean = context
.getBean(ServletRegistrationBean.class);
assertThat(registrationBean.getUrlMappings()).containsExactly(path);
};
}
}

View File

@ -1,105 +0,0 @@
/*
* 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.mvc;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpointContextPathTests.Config;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpointContextPathTests.ContextPathListener;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link JolokiaMvcEndpoint} with a custom management context path.
*
* @author Christian Dupuis
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
@ContextConfiguration(classes = {
Config.class }, initializers = ContextPathListener.class)
@DirtiesContext
public class JolokiaMvcEndpointContextPathTests {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
TestPropertyValues.of("foo:bar")
.applyTo((ConfigurableApplicationContext) this.context);
}
@Test
public void read() throws Exception {
this.mvc.perform(get("/admin/jolokia/read/java.lang:type=Memory"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Configuration
@EnableConfigurationProperties
@EnableWebMvc
@Import({ JacksonAutoConfiguration.class, AuditAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class })
public static class Config {
}
public static class ContextPathListener
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
TestPropertyValues.of("management.contextPath:/admin").applyTo(context);
}
}
}

View File

@ -1,111 +0,0 @@
/*
* 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.mvc;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for {@link JolokiaMvcEndpoint}.
*
* @author Christian Dupuis
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "management.security.enabled=false")
public class JolokiaMvcEndpointIntegrationTests {
@Autowired
private MvcEndpoints endpoints;
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
TestPropertyValues.of("foo:bar")
.applyTo((ConfigurableApplicationContext) this.context);
}
@Test
public void endpointRegistered() throws Exception {
Set<? extends MvcEndpoint> values = this.endpoints.getEndpoints();
assertThat(values).hasAtLeastOneElementOfType(JolokiaMvcEndpoint.class);
}
@Test
public void search() throws Exception {
this.mvc.perform(get("/application/jolokia/search/java.lang:*"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("GarbageCollector")));
}
@Test
public void read() throws Exception {
this.mvc.perform(get("/application/jolokia/read/java.lang:type=Memory"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Test
public void list() throws Exception {
this.mvc.perform(get("/application/jolokia/list/java.lang/type=Memory/attr"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("NonHeapMemoryUsage")));
}
@Configuration
@EnableConfigurationProperties
@EnableWebMvc
@Import({ JacksonAutoConfiguration.class, AuditAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class })
public static class Config {
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2012-2016 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.mvc;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.mvc.ServletWrappingController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link JolokiaMvcEndpoint}.
*
* @author Andy Wilkinson
*/
public class JolokiaMvcEndpointTests {
private final JolokiaMvcEndpoint endpoint = new JolokiaMvcEndpoint();
private final ServletWrappingController controller = (ServletWrappingController) spy(
ReflectionTestUtils.getField(this.endpoint, "controller"));
@Before
public void before() {
ReflectionTestUtils.setField(this.endpoint, "controller", this.controller);
}
@Test
public void controllerIsDestroyed() throws Exception {
this.endpoint.setApplicationContext(mock(WebApplicationContext.class));
this.endpoint.setServletContext(new MockServletContext());
this.endpoint.afterPropertiesSet();
this.endpoint.destroy();
assertThat(this.endpoint).isInstanceOf(DisposableBean.class);
verify(this.controller).destroy();
}
}

View File

@ -22,7 +22,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaManagementContextConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
@ -56,7 +56,8 @@ public class MvcEndpointCorsIntegrationTests {
HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, AuditAutoConfiguration.class,
JolokiaAutoConfiguration.class, WebMvcAutoConfiguration.class);
JolokiaManagementContextConfiguration.class,
WebMvcAutoConfiguration.class);
}
@Test
@ -159,18 +160,6 @@ public class MvcEndpointCorsIntegrationTests {
header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
}
@Test
public void jolokiaEndpointUsesGlobalCorsConfiguration() throws Exception {
TestPropertyValues.of("endpoints.cors.allowed-origins:foo.example.com")
.applyTo(this.context);
createMockMvc()
.perform(options("/application/jolokia")
.header("Origin", "bar.example.com")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"))
.andExpect(status().isForbidden());
performAcceptedCorsRequest("/application/jolokia");
}
private MockMvc createMockMvc() {
this.context.refresh();
return MockMvcBuilders.webAppContextSetup(this.context).build();