diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index e7897af51b..a70ecd13f2 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java @@ -16,14 +16,17 @@ package org.springframework.boot.actuate.autoconfigure; +import javax.servlet.Servlet; import javax.sql.DataSource; +import org.apache.catalina.startup.Tomcat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics; import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.SystemPublicMetrics; +import org.springframework.boot.actuate.endpoint.TomcatPublicMetrics; import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; @@ -43,6 +46,7 @@ import org.springframework.context.annotation.Configuration; * * @author Stephane Nicoll * @author Phillip Webb + * @author Johannes Stelzer * @since 1.2.0 */ @Configuration @@ -71,7 +75,6 @@ public class PublicMetricsAutoConfiguration { return new RichGaugeReaderPublicMetrics(richGaugeReader); } - @Configuration @ConditionalOnClass(DataSource.class) @ConditionalOnBean(DataSource.class) static class DataSourceMetricsConfiguration { @@ -85,4 +88,15 @@ public class PublicMetricsAutoConfiguration { } + @ConditionalOnClass({ Servlet.class, Tomcat.class }) + static class TomcatMetricsConfiguration { + + @Bean + @ConditionalOnMissingBean + public TomcatPublicMetrics tomcatPublicMetrics() { + return new TomcatPublicMetrics(); + } + + } + } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetrics.java new file mode 100644 index 0000000000..6c28dc5940 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetrics.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012-2014 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 java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Manager; +import org.springframework.beans.BeansException; +import org.springframework.boot.actuate.metrics.Metric; +import org.springframework.boot.context.embedded.EmbeddedServletContainer; +import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * A {@link PublicMetrics} implementation that provides Tomcat statistics. + * + * @author Johannes Stelzer + * @author Phillip Webb + * @since 1.2.0 + */ +public class TomcatPublicMetrics implements PublicMetrics, ApplicationContextAware { + + private ApplicationContext applicationContext; + + @Override + public Collection> metrics() { + if (this.applicationContext instanceof EmbeddedWebApplicationContext) { + Manager manager = getManager((EmbeddedWebApplicationContext) this.applicationContext); + if (manager != null) { + return metrics(manager); + } + } + return Collections.emptySet(); + } + + private Manager getManager(EmbeddedWebApplicationContext applicationContext) { + EmbeddedServletContainer embeddedServletContainer = applicationContext + .getEmbeddedServletContainer(); + if (embeddedServletContainer instanceof TomcatEmbeddedServletContainer) { + return getManager((TomcatEmbeddedServletContainer) embeddedServletContainer); + } + return null; + } + + private Manager getManager(TomcatEmbeddedServletContainer servletContainer) { + for (Container container : servletContainer.getTomcat().getHost().findChildren()) { + if (container instanceof Context) { + return ((Context) container).getManager(); + } + } + return null; + } + + private Collection> metrics(Manager manager) { + List> metrics = new ArrayList>(2); + metrics.add(new Metric("httpsessions.max", manager.getMaxActive())); + metrics.add(new Metric("httpsessions.active", manager + .getActiveSessions())); + return metrics; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java index 55f0f5e1d8..e57c478c2f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java @@ -33,12 +33,14 @@ import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.PublicMetrics; import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics; import org.springframework.boot.actuate.endpoint.SystemPublicMetrics; +import org.springframework.boot.actuate.endpoint.TomcatPublicMetrics; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.rich.RichGauge; import org.springframework.boot.actuate.metrics.rich.RichGaugeReader; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,6 +48,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.SocketUtils; import com.zaxxer.hikari.HikariDataSource; @@ -175,6 +178,12 @@ public class PublicMetricsAutoConfigurationTests { "ds.second.usage"); } + @Test + public void tomcatMetrics() throws Exception { + load(TomcatConfiguration.class); + assertEquals(1, this.context.getBeansOfType(TomcatPublicMetrics.class).size()); + } + private void assertHasMetric(Collection> metrics, Metric metric) { for (Metric m : metrics) { if (m.getValue().equals(metric.getValue()) @@ -271,4 +280,16 @@ public class PublicMetricsAutoConfigurationTests { } + @Configuration + static class TomcatConfiguration { + + @Bean + public TomcatEmbeddedServletContainerFactory containerFactory() { + TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); + factory.setPort(SocketUtils.findAvailableTcpPort(40000)); + return factory; + } + + } + } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java new file mode 100644 index 0000000000..6580f28cf3 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2014 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 java.util.Iterator; + +import org.junit.Test; +import org.springframework.boot.actuate.metrics.Metric; +import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.SocketUtils; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link TomcatPublicMetrics} + * + * @author Johannes Stelzer + * @author Phillip Webb + */ +public class TomcatPublicMetricsTests { + + @Test + public void tomcatMetrics() throws Exception { + AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext( + Config.class); + try { + TomcatPublicMetrics tomcatMetrics = context + .getBean(TomcatPublicMetrics.class); + Iterator> metrics = tomcatMetrics.metrics().iterator(); + assertThat(metrics.next().getName(), equalTo("httpsessions.max")); + assertThat(metrics.next().getName(), equalTo("httpsessions.active")); + assertThat(metrics.hasNext(), equalTo(false)); + } + finally { + context.close(); + } + } + + @Configuration + static class Config { + + @Bean + public TomcatEmbeddedServletContainerFactory containerFactory() { + TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); + factory.setPort(SocketUtils.findAvailableTcpPort(40000)); + return factory; + } + + @Bean + public TomcatPublicMetrics metrics() { + return new TomcatPublicMetrics(); + } + + } + +}