From 2d058570ca9834b24b43e3d6387ef5aa5264d0e4 Mon Sep 17 00:00:00 2001 From: Christian Dupuis Date: Fri, 22 Nov 2013 18:05:27 +0100 Subject: [PATCH] Add auto configure support for Jolokia a JMX-HTTP bridge --- spring-boot-actuator/pom.xml | 7 +- .../JolokiaAutoConfiguration.java | 103 +++++++++++++++++ .../actuate/endpoint/JolokiaEndpoint.java | 44 ++++++++ .../main/resources/META-INF/spring.factories | 1 + .../JolokiaAutoConfigurationTests.java | 105 ++++++++++++++++++ .../endpoint/JolokiaEndpointTests.java | 42 +++++++ 6 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java create mode 100644 spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java create mode 100644 spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 0bc11243e5..b1ce2751e4 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -65,7 +65,7 @@ org.springframework.security spring-security-config - true + true org.apache.tomcat.embed @@ -77,6 +77,11 @@ crash.embed.spring true + + org.jolokia + jolokia-core + true + org.springframework diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java new file mode 100644 index 0000000000..030e04f235 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfiguration.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013 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; + +import java.util.Map; + +import org.jolokia.http.AgentServlet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.JolokiaEndpoint; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; +import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for embedding Jolokia, a JMX-HTTP + * bridge giving an alternative to JSR-160 connectors. + * + *

+ * This configuration will get automatically enabled as soon as the Jolokia + * {@link AgentServlet} is on the classpath. To disable set + * endpoints.jolokia.enabled: false. + * + *

+ * Additional configuration parameters for Jolokia can be provided by specifying + * jolokia.config. ... properties. See the http://jolokia.org web site for more information on + * supported configuration parameters. + * + * @author Christian Dupuis + */ +@Configuration +@ConditionalOnWebApplication +@ConditionalOnClass({ AgentServlet.class }) +@ConditionalOnBean(EmbeddedServletContainerFactory.class) +@AutoConfigureBefore(ManagementSecurityAutoConfiguration.class) +@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) +@ConditionalOnExpression("${endpoints.jolokia.enabled:true}") +public class JolokiaAutoConfiguration { + + private RelaxedPropertyResolver environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = new RelaxedPropertyResolver(environment); + } + + @Bean + @ConditionalOnMissingBean({ AgentServlet.class }) + public AgentServlet jolokiaServlet() { + return new AgentServlet(); + } + + @Bean + @ConditionalOnMissingBean() + public ServletRegistrationBean jolokiaServletRegistration() { + ServletRegistrationBean registrationBean = new ServletRegistrationBean( + jolokiaServlet(), this.environment.getProperty("endpoints.jolokia.path", + "/jolokia") + "/*"); + addInitParameters(registrationBean); + return registrationBean; + } + + @Bean + @ConditionalOnMissingBean + public JolokiaEndpoint jolokiaEndpoint() { + return new JolokiaEndpoint(); + } + + protected void addInitParameters(ServletRegistrationBean registrationBean) { + Map configParameters = this.environment + .getSubProperties("jolokia.config."); + for (Map.Entry configParameter : configParameters.entrySet()) { + registrationBean.addInitParameter(configParameter.getKey(), configParameter + .getValue().toString()); + } + } +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java new file mode 100644 index 0000000000..133365bcba --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/JolokiaEndpoint.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 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; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.http.HttpMethod; + +/** + * {@link Endpoint} implementation to register the Jolokia infrastructure with the Boot + * management subsystem. + * + * @author Christian Dupuis + */ +@ConfigurationProperties(name = "endpoints.jolokia", ignoreUnknownFields = false) +public class JolokiaEndpoint extends AbstractEndpoint { + + public JolokiaEndpoint() { + super("/jolokia"); + } + + @Override + public String invoke() { + return null; + } + + @Override + public HttpMethod[] methods() { + return NO_HTTP_METHOD; + } +} diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index 18e4f40631..bc44be858f 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -4,6 +4,7 @@ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ErrorMvcAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.JolokiaAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java new file mode 100644 index 0000000000..121c26d78c --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013 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; + +import javax.servlet.Servlet; +import javax.servlet.ServletRegistration; + +import org.jolokia.http.AgentServlet; +import org.junit.After; +import org.junit.Test; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory.RegisteredServlet; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Tests for {@link JolokiaAutoConfiguration}. + * + * @author Christian Dupuis + */ +public class JolokiaAutoConfigurationTests { + + private AnnotationConfigEmbeddedWebApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + if (Config.containerFactory != null) { + Config.containerFactory = null; + } + } + + @Test + public void agentServletRegisteredWithAppContext() throws Exception { + this.context = new AnnotationConfigEmbeddedWebApplicationContext(); + this.context.register(Config.class, WebMvcAutoConfiguration.class, + JolokiaAutoConfiguration.class); + this.context.refresh(); + assertEquals(1, this.context.getBeanNamesForType(AgentServlet.class).length); + } + + @Test + public void agentServletRegisteredWithServletContainer() throws Exception { + this.context = new AnnotationConfigEmbeddedWebApplicationContext(); + this.context.register(Config.class, WebMvcAutoConfiguration.class, + JolokiaAutoConfiguration.class); + this.context.refresh(); + + Servlet servlet = null; + ServletRegistration.Dynamic registration = null; + for (RegisteredServlet registeredServlet : Config.containerFactory.getContainer() + .getRegisteredServlets()) { + if (registeredServlet.getServlet() instanceof AgentServlet) { + servlet = registeredServlet.getServlet(); + registration = registeredServlet.getRegistration(); + } + } + assertNotNull(servlet); + assertNotNull(registration); + } + + @Configuration + protected static class Config { + + protected static MockEmbeddedServletContainerFactory containerFactory = null; + + @Bean + public EmbeddedServletContainerFactory containerFactory() { + if (containerFactory == null) { + containerFactory = new MockEmbeddedServletContainerFactory(); + } + return containerFactory; + } + + @Bean + public EmbeddedServletContainerCustomizerBeanPostProcessor embeddedServletContainerCustomizerBeanPostProcessor() { + return new EmbeddedServletContainerCustomizerBeanPostProcessor(); + } + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java new file mode 100644 index 0000000000..99485f02d5 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/JolokiaEndpointTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013 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; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Christian Dupuis + */ +public class JolokiaEndpointTests extends AbstractEndpointTests { + + public JolokiaEndpointTests() { + super(Config.class, JolokiaEndpoint.class, "/jolokia", true, "endpoints.jolokia"); + } + + @Configuration + @EnableConfigurationProperties + public static class Config { + + @Bean + public JolokiaEndpoint endpoint() { + return new JolokiaEndpoint(); + } + + } +}