diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java index 155e343f6c0..492a2c48304 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.web.embedded.NettyWebServerFactory import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.reactive.TomcatReactiveWebServerFactoryCustomizer; import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -65,6 +66,7 @@ public class ReactiveManagementChildContextConfiguration { ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) { super(beanFactory, ReactiveWebServerFactoryCustomizer.class, TomcatWebServerFactoryCustomizer.class, + TomcatReactiveWebServerFactoryCustomizer.class, JettyWebServerFactoryCustomizer.class, UndertowWebServerFactoryCustomizer.class, NettyWebServerFactoryCustomizer.class); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java index 7dab04e3c61..56e9b5dd347 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfigurationTests.java @@ -17,10 +17,12 @@ package org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat; import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.apache.tomcat.util.modeler.Registry; import org.junit.jupiter.api.Test; import org.springframework.boot.SpringApplication; @@ -33,11 +35,13 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -51,6 +55,7 @@ public class TomcatMetricsAutoConfigurationTests { @Test public void autoConfiguresTomcatMetricsWithEmbeddedServletTomcat() { + resetTomcatState(); new WebApplicationContextRunner( AnnotationConfigServletWebServerApplicationContext::new) .withConfiguration(AutoConfigurations.of( @@ -58,6 +63,7 @@ public class TomcatMetricsAutoConfigurationTests { ServletWebServerFactoryAutoConfiguration.class)) .withUserConfiguration(ServletWebServerConfiguration.class, MeterRegistryConfiguration.class) + .withPropertyValues("server.tomcat.mbeanregistry.enabled=true") .run((context) -> { context.publishEvent( new ApplicationStartedEvent(new SpringApplication(), @@ -75,6 +81,7 @@ public class TomcatMetricsAutoConfigurationTests { @Test public void autoConfiguresTomcatMetricsWithEmbeddedReactiveTomcat() { + resetTomcatState(); new ReactiveWebApplicationContextRunner( AnnotationConfigReactiveWebServerApplicationContext::new) .withConfiguration(AutoConfigurations.of( @@ -82,6 +89,7 @@ public class TomcatMetricsAutoConfigurationTests { ReactiveWebServerFactoryAutoConfiguration.class)) .withUserConfiguration(ReactiveWebServerConfiguration.class, MeterRegistryConfiguration.class) + .withPropertyValues("server.tomcat.mbeanregistry.enabled=true") .run((context) -> { context.publishEvent( new ApplicationStartedEvent(new SpringApplication(), @@ -130,6 +138,13 @@ public class TomcatMetricsAutoConfigurationTests { .hasBean("customTomcatMetrics")); } + private void resetTomcatState() { + ReflectionTestUtils.setField(Registry.class, "registry", null); + AtomicInteger containerCounter = (AtomicInteger) ReflectionTestUtils + .getField(TomcatWebServer.class, "containerCounter"); + containerCounter.set(-1); + } + @Configuration(proxyBeanMethods = false) static class MeterRegistryConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java index 2bd342a35c7..715d2a778d7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/BackgroundPreinitializer.java @@ -23,8 +23,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.validation.Configuration; import javax.validation.Validation; -import org.apache.catalina.mbeans.MBeanFactory; - import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationStartingEvent; @@ -99,7 +97,6 @@ public class BackgroundPreinitializer runSafely(new ConversionServiceInitializer()); runSafely(new ValidationInitializer()); runSafely(new MessageConverterInitializer()); - runSafely(new MBeanFactoryInitializer()); runSafely(new JacksonInitializer()); runSafely(new CharsetInitializer()); preinitializationComplete.countDown(); @@ -137,18 +134,6 @@ public class BackgroundPreinitializer } - /** - * Early initializer to load Tomcat MBean XML. - */ - private static class MBeanFactoryInitializer implements Runnable { - - @Override - public void run() { - new MBeanFactory(); - } - - } - /** * Early initializer for javax.validation. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index b4eaf94062b..6bcf4a6126a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -400,6 +400,11 @@ public class ServerProperties { */ private final Resource resource = new Resource(); + /** + * Modeler MBean Registry configuration. + */ + private final Mbeanregistry mbeanregistry = new Mbeanregistry(); + public int getMaxThreads() { return this.maxThreads; } @@ -552,6 +557,10 @@ public class ServerProperties { return this.resource; } + public Mbeanregistry getMbeanregistry() { + return this.mbeanregistry; + } + /** * Tomcat access log properties. */ @@ -821,6 +830,23 @@ public class ServerProperties { } + public static class Mbeanregistry { + + /** + * Whether Tomcat's MBean Registry should be enabled. + */ + private boolean enabled; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java index d696d52a94d..7f55a9ff00d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java @@ -24,6 +24,7 @@ import reactor.netty.http.server.HttpServer; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; @@ -98,6 +99,12 @@ abstract class ReactiveWebServerFactoryConfiguration { return factory; } + @Bean + public TomcatReactiveWebServerFactoryCustomizer tomcatReactiveWebServerFactoryCustomizer( + ServerProperties serverProperties) { + return new TomcatReactiveWebServerFactoryCustomizer(serverProperties); + } + } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/TomcatReactiveWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/TomcatReactiveWebServerFactoryCustomizer.java new file mode 100644 index 00000000000..7f8ba6f8b7e --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/TomcatReactiveWebServerFactoryCustomizer.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.web.reactive; + +import org.apache.tomcat.util.modeler.Registry; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; + +/** + * {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to Tomcat reactive + * web servers. + * + * @author Andy Wilkinson + * @since 2.2.0 + */ +public class TomcatReactiveWebServerFactoryCustomizer + implements WebServerFactoryCustomizer { + + private final ServerProperties serverProperties; + + public TomcatReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) { + this.serverProperties = serverProperties; + } + + @Override + public void customize(TomcatReactiveWebServerFactory factory) { + if (!this.serverProperties.getTomcat().getMbeanregistry().isEnabled()) { + Registry.disableRegistry(); + } + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java index 236fbb477e7..93f488acc01 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.web.servlet; +import org.apache.tomcat.util.modeler.Registry; + import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; @@ -60,6 +62,9 @@ public class TomcatServletWebServerFactoryCustomizer customizeUseRelativeRedirects(factory, tomcatProperties.getUseRelativeRedirects()); } + if (!tomcatProperties.getMbeanregistry().isEnabled()) { + Registry.disableRegistry(); + } } private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index a4975aeb9c7..f3e9fce9d3a 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -1075,6 +1075,20 @@ include::{code-examples}/context/embedded/TomcatLegacyCookieProcessorExample.jav +[[howto-enable-tomcat-mbean-registry]] +=== Enable Tomcat's MBean Registry +Embedded Tomcat's MBean registry is disabled by default. This minimizes Tomcat's memory +footprint. If you want to use Tomcat's MBeans, for example so that they can be used to +expose metrics via Micrometer, you must use the `server.tomcat.mbeanregistry.enabled` +property to do so, as shown in the following example: + +[source,properties,indent=0] +---- +server.tomcat.mbeanregistry.enabled=true +---- + + + [[howto-enable-multiple-listeners-in-undertow]] === Enable Multiple Listeners with Undertow Add an `UndertowBuilderCustomizer` to the `UndertowServletWebServerFactory` and diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 9edd3939527..7113dcdba8c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -1793,7 +1793,8 @@ Spring Boot registers the following core metrics when applicable: * Logback metrics: record the number of events logged to Logback at each level * Uptime metrics: report a gauge for uptime and a fixed gauge representing the application's absolute start time -* Tomcat metrics +* Tomcat metrics (`server.tomcat.mbeanregistry.enabled` must be set to `true` for all +Tomcat metrics to be registered) * https://docs.spring.io/spring-integration/docs/current/reference/html/system-management-chapter.html#micrometer-integration[Spring Integration] metrics