Commit 7e97495c authored by Stephane Nicoll's avatar Stephane Nicoll Committed by Andy Wilkinson

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
parent 2eb3da5b
...@@ -16,44 +16,31 @@ ...@@ -16,44 +16,31 @@
package org.springframework.boot.actuate.autoconfigure.jolokia; package org.springframework.boot.actuate.autoconfigure.jolokia;
import java.util.Properties;
import org.jolokia.http.AgentServlet; import org.jolokia.http.AgentServlet;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration.JolokiaCondition; import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.security.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
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.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean; 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; import org.springframework.web.servlet.mvc.ServletWrappingController;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for embedding Jolokia, a JMX-HTTP * {@link ManagementContextConfiguration} for embedding Jolokia, a JMX-HTTP bridge giving
* bridge giving an alternative to JSR-160 connectors. * an alternative to JSR-160 connectors.
* *
* <p> * <p>
* This configuration will get automatically enabled as soon as the Jolokia * This configuration will get automatically enabled as soon as the Jolokia
* {@link AgentServlet} is on the classpath. To disable it set * {@link AgentServlet} is on the classpath. To disable it set
* {@code endpoints.jolokia.enabled: false} or {@code endpoints.enabled: false}. * {@code management.jolokia.enabled=false}.
* *
* <p> * <p>
* Additional configuration parameters for Jolokia can be provided by specifying * Additional configuration parameters for Jolokia can be provided by specifying
* {@code jolokia.config.*} properties. See the * {@code management.jolokia.config.*} properties. See the
* <a href="http://jolokia.org">http://jolokia.org</a> web site for more information on * <a href="http://jolokia.org">http://jolokia.org</a> web site for more information on
* supported configuration parameters. * supported configuration parameters.
* *
...@@ -61,59 +48,36 @@ import org.springframework.web.servlet.mvc.ServletWrappingController; ...@@ -61,59 +48,36 @@ import org.springframework.web.servlet.mvc.ServletWrappingController;
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Madhura Bhave * @author Madhura Bhave
* @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration @ManagementContextConfiguration
@ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ AgentServlet.class, ServletWrappingController.class }) @ConditionalOnClass({ AgentServlet.class, ServletWrappingController.class })
@Conditional(JolokiaCondition.class) @ConditionalOnProperty(value = "management.jolokia.enabled", matchIfMissing = true)
@AutoConfigureBefore(ManagementWebSecurityAutoConfiguration.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(JolokiaProperties.class) @EnableConfigurationProperties(JolokiaProperties.class)
public class JolokiaAutoConfiguration { public class JolokiaManagementContextConfiguration {
private final ManagementServletContext managementServletContext;
private final JolokiaProperties properties; private final JolokiaProperties properties;
public JolokiaAutoConfiguration(JolokiaProperties properties) { public JolokiaManagementContextConfiguration(
ManagementServletContext managementServletContext,
JolokiaProperties properties) {
this.managementServletContext = managementServletContext;
this.properties = properties; this.properties = properties;
} }
@Bean @Bean
@ConditionalOnMissingBean public ServletRegistrationBean<AgentServlet> jolokiaServlet() {
public JolokiaMvcEndpoint jolokiaEndpoint() { String path = this.managementServletContext.getContextPath()
JolokiaMvcEndpoint endpoint = new JolokiaMvcEndpoint(); + this.properties.getPath();
endpoint.setInitParameters(getInitParameters()); String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*");
return endpoint; ServletRegistrationBean<AgentServlet> registration = new ServletRegistrationBean<>(
} new AgentServlet(), urlMapping);
registration.setInitParameters(this.properties.getConfig());
private Properties getInitParameters() { return registration;
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);
}
} }
} }
...@@ -26,23 +26,46 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -26,23 +26,46 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* *
* @author Christian Dupuis * @author Christian Dupuis
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0
*/ */
@ConfigurationProperties(prefix = "jolokia") @ConfigurationProperties(prefix = "management.jolokia")
public class JolokiaProperties { 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 * Jolokia settings. These are traditionally set using servlet parameters. Refer to
* the documentation of Jolokia for more details. * the documentation of Jolokia for more details.
*/ */
private Map<String, String> config = new HashMap<>(); private final Map<String, String> config = new HashMap<>();
public Map<String, String> getConfig() { public boolean isEnabled() {
return this.config; return this.enabled;
} }
public void setConfig(Map<String, String> config) { public void setEnabled(boolean enabled) {
this.config = config; 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;
} }
} }
/*
* 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;
}
}
}
...@@ -6,7 +6,6 @@ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoC ...@@ -6,7 +6,6 @@ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoC
org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration,\ 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.security.ManagementWebSecurityAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration,\
...@@ -19,4 +18,5 @@ org.springframework.boot.actuate.autoconfigure.trace.TraceWebFilterAutoConfigura ...@@ -19,4 +18,5 @@ org.springframework.boot.actuate.autoconfigure.trace.TraceWebFilterAutoConfigura
org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryActuatorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\ 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
...@@ -20,7 +20,6 @@ import org.junit.After; ...@@ -20,7 +20,6 @@ import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoConfiguration; 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.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
...@@ -74,8 +73,7 @@ public class SpringApplicationHierarchyTests { ...@@ -74,8 +73,7 @@ public class SpringApplicationHierarchyTests {
} }
@EnableAutoConfiguration(exclude = { JolokiaAutoConfiguration.class, @EnableAutoConfiguration(exclude = { EndpointMBeanExportAutoConfiguration.class,
EndpointMBeanExportAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class, ElasticsearchRepositoriesAutoConfiguration.class,
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class, CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
......
...@@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters; ...@@ -28,7 +28,7 @@ import org.junit.runners.Parameterized.Parameters;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration; 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.AutoConfigurationReportEndpoint;
import org.springframework.boot.actuate.endpoint.BeansEndpoint; import org.springframework.boot.actuate.endpoint.BeansEndpoint;
import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint; import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint;
...@@ -43,7 +43,6 @@ import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint; ...@@ -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.EndpointMvcAdapter;
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; 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.LogFileMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
...@@ -96,7 +95,6 @@ public class MvcEndpointPathConfigurationTests { ...@@ -96,7 +95,6 @@ public class MvcEndpointPathConfigurationTests {
new Object[] { "flyway", FlywayEndpoint.class }, new Object[] { "flyway", FlywayEndpoint.class },
new Object[] { "health", HealthMvcEndpoint.class }, new Object[] { "health", HealthMvcEndpoint.class },
new Object[] { "info", InfoEndpoint.class }, new Object[] { "info", InfoEndpoint.class },
new Object[] { "jolokia", JolokiaMvcEndpoint.class },
new Object[] { "liquibase", LiquibaseEndpoint.class }, new Object[] { "liquibase", LiquibaseEndpoint.class },
new Object[] { "logfile", LogFileMvcEndpoint.class }, new Object[] { "logfile", LogFileMvcEndpoint.class },
new Object[] { "loggers", LoggersMvcEndpoint.class }, new Object[] { "loggers", LoggersMvcEndpoint.class },
...@@ -144,7 +142,8 @@ public class MvcEndpointPathConfigurationTests { ...@@ -144,7 +142,8 @@ public class MvcEndpointPathConfigurationTests {
@Configuration @Configuration
@ImportAutoConfiguration({ EndpointAutoConfiguration.class, @ImportAutoConfiguration({ EndpointAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, AuditAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, AuditAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class }) EndpointWebMvcAutoConfiguration.class,
JolokiaManagementContextConfiguration.class })
protected static class TestConfiguration { protected static class TestConfiguration {
......
/*
* 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();
}
}
}
...@@ -14,91 +14,105 @@ ...@@ -14,91 +14,105 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.endpoint.mvc; 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.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; 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.endpoint.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
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.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; 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;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; 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.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; 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.assertj.core.api.Assertions.assertThat;
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. * Integration tests for {@link JolokiaManagementContextConfiguration}.
* *
* @author Christian Dupuis * @author Stephane Nicoll
* @author Dave Syer
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = "management.security.enabled=false")
@ContextConfiguration(classes = {
Config.class }, initializers = ContextPathListener.class)
@DirtiesContext @DirtiesContext
public class JolokiaMvcEndpointContextPathTests { @TestPropertySource(properties = "management.security.enabled=false")
public class JolokiaManagementContextConfigurationIntegrationTests {
@Autowired @Autowired
private WebApplicationContext context; 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\"");
}
private MockMvc mvc; @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");
}
@Before @Test
public void setUp() { public void read() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); ResponseEntity<String> response = this.restTemplate.getForEntity(
TestPropertyValues.of("foo:bar") "/application/jolokia/read/java.lang:type=Memory", String.class);
.applyTo((ConfigurableApplicationContext) this.context); assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("NonHeapMemoryUsage");
} }
@Test @Test
public void read() throws Exception { public void list() {
this.mvc.perform(get("/admin/jolokia/read/java.lang:type=Memory")) ResponseEntity<String> response = this.restTemplate.getForEntity(
.andExpect(status().isOk()) "/application/jolokia/list/java.lang/type=Memory/attr", String.class);
.andExpect(content().string(containsString("NonHeapMemoryUsage"))); assertThat(HttpStatus.OK).isEqualTo(response.getStatusCode());
assertThat(response.getBody()).contains("NonHeapMemoryUsage");
} }
@Configuration @Configuration
@EnableConfigurationProperties @MinimalWebConfiguration
@EnableWebMvc @Import({ JacksonAutoConfiguration.class,
@Import({ JacksonAutoConfiguration.class, AuditAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, JolokiaAutoConfiguration.class }) EndpointWebMvcAutoConfiguration.class,
public static class Config { JolokiaManagementContextConfiguration.class })
protected static class Application {
} }
public static class ContextPathListener @Target(ElementType.TYPE)
implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Retention(RetentionPolicy.RUNTIME)
@Documented
@Override @Import({ ServletWebServerFactoryAutoConfiguration.class,
public void initialize(ConfigurableApplicationContext context) { DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class,
TestPropertyValues.of("management.contextPath:/admin").applyTo(context); WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
} ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration {
} }
......
/*
* 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);
};
}
}
/*
* 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 {
}
}
/*
* 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();
}
}
...@@ -22,7 +22,7 @@ import org.junit.Test; ...@@ -22,7 +22,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration; 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.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
...@@ -56,7 +56,8 @@ public class MvcEndpointCorsIntegrationTests { ...@@ -56,7 +56,8 @@ public class MvcEndpointCorsIntegrationTests {
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, AuditAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, AuditAutoConfiguration.class,
JolokiaAutoConfiguration.class, WebMvcAutoConfiguration.class); JolokiaManagementContextConfiguration.class,
WebMvcAutoConfiguration.class);
} }
@Test @Test
...@@ -159,18 +160,6 @@ public class MvcEndpointCorsIntegrationTests { ...@@ -159,18 +160,6 @@ public class MvcEndpointCorsIntegrationTests {
header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); 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() { private MockMvc createMockMvc() {
this.context.refresh(); this.context.refresh();
return MockMvcBuilders.webAppContextSetup(this.context).build(); return MockMvcBuilders.webAppContextSetup(this.context).build();
......
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