From c661a74c7540c5650827ebeeb30ea889dc8892b7 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Thu, 26 Sep 2013 19:19:11 -0400 Subject: [PATCH] Add TomcatConnectorCustomizer ... and ServerProperties.Tomcat.maxThreads to conveniently limit the thread pool size in the Connector. --- .../embedded/properties/ServerProperties.java | 28 ++++++++++++- .../tomcat/TomcatConnectorCustomizer.java | 31 +++++++++++++++ ...TomcatEmbeddedServletContainerFactory.java | 39 +++++++++++++++++++ ...tEmbeddedServletContainerFactoryTests.java | 17 ++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatConnectorCustomizer.java diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/properties/ServerProperties.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/properties/ServerProperties.java index 2efc2085b2..b6d3996b7a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/properties/ServerProperties.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/properties/ServerProperties.java @@ -22,12 +22,16 @@ import java.net.InetAddress; import javax.validation.constraints.NotNull; import org.apache.catalina.Context; +import org.apache.catalina.connector.Connector; import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.RemoteIpValve; +import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.ProtocolHandler; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -119,6 +123,16 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { private int backgroundProcessorDelay = 30; // seconds + private int maxThreads = 0; // Number of threads in protocol handler + + public int getMaxThreads() { + return this.maxThreads; + } + + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } + public boolean getAccessLogEnabled() { return this.accessLogEnabled; } @@ -189,6 +203,19 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { factory.addContextValves(valve); } + if (this.maxThreads > 0) { + factory.addConnectorCustomizers(new TomcatConnectorCustomizer() { + @Override + public void customize(Connector connector) { + ProtocolHandler handler = connector.getProtocolHandler(); + if (handler instanceof AbstractProtocol) { + AbstractProtocol protocol = (AbstractProtocol) handler; + protocol.setMaxThreads(Tomcat.this.maxThreads); + } + } + }); + } + if (this.accessLogEnabled) { AccessLogValve valve = new AccessLogValve(); String accessLogPattern = getAccessLogPattern(); @@ -202,7 +229,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { factory.addContextValves(valve); } } - } } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatConnectorCustomizer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatConnectorCustomizer.java new file mode 100644 index 0000000000..82bf11995f --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatConnectorCustomizer.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-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.context.embedded.tomcat; + +import org.apache.catalina.connector.Connector; + +/** + * @author Dave Syer + */ +public interface TomcatConnectorCustomizer { + + /** + * @param connector the connector to customize + */ + void customize(Connector connector); + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index 27772be06f..19ea0b593b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -84,6 +84,8 @@ public class TomcatEmbeddedServletContainerFactory extends private List tomcatContextCustomizers = new ArrayList(); + private List tomcatConnectorCustomizers = new ArrayList(); + private ResourceLoader resourceLoader; private String protocol = DEFAULT_PROTOCOL; @@ -203,6 +205,9 @@ public class TomcatEmbeddedServletContainerFactory extends // If ApplicationContext is slow to start we want Tomcat not to bind to the socket // prematurely... connector.setProperty("bindOnInit", "false"); + for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { + customizer.customize(connector); + } } /** @@ -384,6 +389,40 @@ public class TomcatEmbeddedServletContainerFactory extends this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers)); } + /** + * Set {@link TomcatConnectorCustomizer}s that should be applied to the Tomcat + * {@link Connector} . Calling this method will replace any existing customizers. + * @param tomcatConnectorCustomizers the customizers to set + */ + public void setTomcatConnectorCustomizers( + Collection tomcatConnectorCustomizers) { + Assert.notNull(this.contextLifecycleListeners, + "TomcatConnectorCustomizer must not be null"); + this.tomcatConnectorCustomizers = new ArrayList( + tomcatConnectorCustomizers); + } + + /** + * Add {@link TomcatContextCustomizer}s that should be added to the Tomcat + * {@link Connector}. + * @param tomcatConnectorCustomizers the customizers to add + */ + public void addConnectorCustomizers( + TomcatConnectorCustomizer... tomcatConnectorCustomizers) { + Assert.notNull(this.tomcatContextCustomizers, + "TomcatConnectorCustomizer must not be null"); + this.tomcatConnectorCustomizers.addAll(Arrays.asList(tomcatConnectorCustomizers)); + } + + /** + * Returns a mutable collection of the {@link TomcatContextCustomizer}s that will be + * applied to the Tomcat {@link Context} . + * @return the tomcatContextCustomizers the listeners that will be applied + */ + public Collection getTomcatConnectorCustomizers() { + return this.tomcatConnectorCustomizers; + } + private static class TomcatErrorPage { private String location; diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java index 2d389e5241..350f358337 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java @@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit; import org.apache.catalina.Context; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; +import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.junit.Test; import org.mockito.InOrder; @@ -80,6 +81,22 @@ public class TomcatEmbeddedServletContainerFactoryTests extends } } + @Test + public void tomcatConnectorCustomizers() throws Exception { + TomcatEmbeddedServletContainerFactory factory = getFactory(); + TomcatConnectorCustomizer[] listeners = new TomcatConnectorCustomizer[4]; + for (int i = 0; i < listeners.length; i++) { + listeners[i] = mock(TomcatConnectorCustomizer.class); + } + factory.setTomcatConnectorCustomizers(Arrays.asList(listeners[0], listeners[1])); + factory.addConnectorCustomizers(listeners[2], listeners[3]); + this.container = factory.getEmbeddedServletContainer(); + InOrder ordered = inOrder((Object[]) listeners); + for (TomcatConnectorCustomizer listener : listeners) { + ordered.verify(listener).customize((Connector) anyObject()); + } + } + @Test public void sessionTimeout() throws Exception { TomcatEmbeddedServletContainerFactory factory = getFactory();