一、何謂BeanProcessor
BeanPostProcessor是SpringFramework裡非常重要的核心接口之一,我先貼出一段源代碼:
/*
* Copyright 2002-2015 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.beans.factory.config;
import org.springframework.beans.BeansException;
/**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
View Code
在這裡我先簡單解釋一下其注釋的含義:
這個接口允許我們自定義修改新bean的一個執行個體,比如說:檢查它們的接口或者将他們包裝成代理對象等,ApplicationContexts能自動察覺到我們在BeanProcessor裡對對象作出的改變,并在後來建立該對象時應用其對應的改變。
這兩個方法分别對應IOC容器對對象初始化前的操作和對象初始化後的操作
下面我們來示範一個例子:
StudentEntity:
package org.hzgj.spring.study.entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class StudentEntity {
private Integer id;
private String name;
private Map<String,String> memerories= new HashMap<>();
public Map<String, String> getMemerories() {
return memerories;
}
public void setMemerories(Map<String, String> memerories) {
this.memerories = memerories;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public StudentEntity() {
// System.out.println("studentEntity initializer....");
}
@Override
public String toString() {
return "StudentEntity{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
private List<String> hobbies=new ArrayList<>();
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
}
TestBeanPostProcessor:
package org.hzgj.spring.study.context;
import org.hzgj.spring.study.entity.StudentEntity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class TestBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof StudentEntity) {
StudentEntity studentEntity = new StudentEntity();
studentEntity.setName(beanName);
return studentEntity;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof StudentEntity)
System.out.println(bean);
return bean;
}
}
Main方法:
package org.hzgj.spring.study;
import org.hzgj.spring.study.entity.StudentEntity;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
StudentEntity studentEntity = applicationContext.getBean(StudentEntity.class);
System.out.println(studentEntity.getName());
}
}
spring-config.xml關鍵代碼:
<bean id="studentEntity" class="org.hzgj.spring.study.entity.StudentEntity" depends-on="studentServiceWithFactory">
<property name="hobbies" ref="hobbies">
</property>
<property name="memerories">
<map>
<entry key="glad" value="play"/>
<entry key="cry" value="cry"/>
</map>
</property>
<property name="name" value="admin"/>
</bean>
<bean id="beanPostProcessor" class="org.hzgj.spring.study.context.TestBeanPostProcessor" />
運作得到如下結果:
我們可以看到在配置檔案裡定義的bean屬性已經發生改變
二、SpringFramework中BeanPostProcessor經典應用場景
1.初始化BeanPostProcessor的源碼
根據 ClassPathXmlApplicationContext的構造方法,我們可以看到該類初始化的時候會調用refresh():
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
那麼緊接着在AbstractApplicationContext中找到refresh()方法:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
那麼在這裡Spring會進行一系列的初始化操作,我們請留意registerBeanPostProcessors(beanFactory);這句代碼,追蹤一下我們可以看到在該方法裡注冊Processor:
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
2.@Autowired實作的代碼跟蹤
注意AutowiredAnnotationBeanPostProcessor最終也是BeanPostProcessor的實作類,具體類的描述我就不再這裡闡述了,大家可自行檢視源碼
3、SpringBoot中的定制化内嵌web容器
這個EmbeddedServletContainerCustomizerBeanPostProcessor直接實作的就是BeanPostProcessor,該類下請關注如下方法:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) {
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
this.beanFactory
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
這裡面有一個接口:EmbeddedServletContainerCustomizer 該接口有個實作類 ServerProperties 熟悉springboot外部化配置原理的同胞們其實一看便知
/*
* Copyright 2012-2018 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.autoconfigure.web;
import java.io.File;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.springframework.boot.autoconfigure.web.ServerProperties.Session.Cookie;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
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.InitParameterConfiguringServletContextInitializer;
import org.springframework.boot.context.embedded.JspServlet;
import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
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.embedded.undertow.UndertowBuilderCustomizer;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@link ConfigurationProperties} for a web server (e.g. port and path settings). Will be
* used to customize an {@link EmbeddedServletContainerFactory} when an
* {@link EmbeddedServletContainerCustomizerBeanPostProcessor} is active.
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Ivan Sopov
* @author Marcos Barbero
* @author Eddú Meléndez
* @author Quinten De Swaef
* @author Venil Noronha
* @author Aurélien Leboulanger
*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind to.
*/
private InetAddress address;
/**
* Context path of the application.
*/
private String contextPath;
/**
* Display name of the application.
*/
private String displayName = "application";
@NestedConfigurationProperty
private ErrorProperties error = new ErrorProperties();
/**
* Path of the main dispatcher servlet.
*/
private String servletPath = "/";
/**
* ServletContext parameters.
*/
private final Map<String, String> contextParameters = new HashMap<String, String>();
/**
* If X-Forwarded-* headers should be applied to the HttpRequest.
*/
private Boolean useForwardHeaders;
/**
* Value to use for the Server response header (no header is sent if empty).
*/
private String serverHeader;
/**
* Maximum size in bytes of the HTTP message header.
*/
private int maxHttpHeaderSize = 0; // bytes
/**
* Maximum size in bytes of the HTTP post content.
*/
private int maxHttpPostSize = 0; // bytes
/**
* Time in milliseconds that connectors will wait for another HTTP request before
* closing the connection. When not set, the connector's container-specific default
* will be used. Use a value of -1 to indicate no (i.e. infinite) timeout.
*/
private Integer connectionTimeout;
private Session session = new Session();
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private Compression compression = new Compression();
@NestedConfigurationProperty
private JspServlet jspServlet;
private final Tomcat tomcat = new Tomcat();
private final Jetty jetty = new Jetty();
private final Undertow undertow = new Undertow();
private Environment environment;
@Override
public int getOrder() {
return 0;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (getPort() != null) {
container.setPort(getPort());
}
if (getAddress() != null) {
container.setAddress(getAddress());
}
if (getContextPath() != null) {
container.setContextPath(getContextPath());
}
if (getDisplayName() != null) {
container.setDisplayName(getDisplayName());
}
if (getSession().getTimeout() != null) {
container.setSessionTimeout(getSession().getTimeout());
}
container.setPersistSession(getSession().isPersistent());
container.setSessionStoreDir(getSession().getStoreDir());
if (getSsl() != null) {
container.setSsl(getSsl());
}
if (getJspServlet() != null) {
container.setJspServlet(getJspServlet());
}
if (getCompression() != null) {
container.setCompression(getCompression());
}
container.setServerHeader(getServerHeader());
if (container instanceof TomcatEmbeddedServletContainerFactory) {
getTomcat().customizeTomcat(this,
(TomcatEmbeddedServletContainerFactory) container);
}
if (container instanceof JettyEmbeddedServletContainerFactory) {
getJetty().customizeJetty(this,
(JettyEmbeddedServletContainerFactory) container);
}
if (container instanceof UndertowEmbeddedServletContainerFactory) {
getUndertow().customizeUndertow(this,
(UndertowEmbeddedServletContainerFactory) container);
}
container.addInitializers(new SessionConfiguringInitializer(this.session));
container.addInitializers(new InitParameterConfiguringServletContextInitializer(
getContextParameters()));
}
public String getServletMapping() {
if (this.servletPath.equals("") || this.servletPath.equals("/")) {
return "/";
}
if (this.servletPath.contains("*")) {
return this.servletPath;
}
if (this.servletPath.endsWith("/")) {
return this.servletPath + "*";
}
return this.servletPath + "/*";
}
public String getPath(String path) {
String prefix = getServletPrefix();
if (!path.startsWith("/")) {
path = "/" + path;
}
return prefix + path;
}
public String getServletPrefix() {
String result = this.servletPath;
if (result.contains("*")) {
result = result.substring(0, result.indexOf("*"));
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
public String[] getPathsArray(Collection<String> paths) {
String[] result = new String[paths.size()];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
public String[] getPathsArray(String[] paths) {
String[] result = new String[paths.length];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
public void setLoader(String value) {
// no op to support Tomcat running as a traditional container (not embedded)
}
public Integer getPort() {
return this.port;
}
public void setPort(Integer port) {
this.port = port;
}
public InetAddress getAddress() {
return this.address;
}
public void setAddress(InetAddress address) {
this.address = address;
}
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = cleanContextPath(contextPath);
}
private String cleanContextPath(String contextPath) {
if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
return contextPath.substring(0, contextPath.length() - 1);
}
return contextPath;
}
public String getDisplayName() {
return this.displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getServletPath() {
return this.servletPath;
}
public void setServletPath(String servletPath) {
Assert.notNull(servletPath, "ServletPath must not be null");
this.servletPath = servletPath;
}
public Map<String, String> getContextParameters() {
return this.contextParameters;
}
public Boolean isUseForwardHeaders() {
return this.useForwardHeaders;
}
public void setUseForwardHeaders(Boolean useForwardHeaders) {
this.useForwardHeaders = useForwardHeaders;
}
public String getServerHeader() {
return this.serverHeader;
}
public void setServerHeader(String serverHeader) {
this.serverHeader = serverHeader;
}
public int getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize;
}
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
@Deprecated
@DeprecatedConfigurationProperty(reason = "Use dedicated property for each container.")
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
@Deprecated
public void setMaxHttpPostSize(int maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
this.jetty.setMaxHttpPostSize(maxHttpPostSize);
this.tomcat.setMaxHttpPostSize(maxHttpPostSize);
this.undertow.setMaxHttpPostSize(maxHttpPostSize);
}
protected final boolean getOrDeduceUseForwardHeaders() {
if (this.useForwardHeaders != null) {
return this.useForwardHeaders;
}
CloudPlatform platform = CloudPlatform.getActive(this.environment);
return (platform == null ? false : platform.isUsingForwardHeaders());
}
public Integer getConnectionTimeout() {
return this.connectionTimeout;
}
public void setConnectionTimeout(Integer connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public ErrorProperties getError() {
return this.error;
}
public Session getSession() {
return this.session;
}
public void setSession(Session session) {
this.session = session;
}
public Ssl getSsl() {
return this.ssl;
}
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
public Compression getCompression() {
return this.compression;
}
public JspServlet getJspServlet() {
return this.jspServlet;
}
public void setJspServlet(JspServlet jspServlet) {
this.jspServlet = jspServlet;
}
public Tomcat getTomcat() {
return this.tomcat;
}
public Jetty getJetty() {
return this.jetty;
}
public Undertow getUndertow() {
return this.undertow;
}
public static class Session {
/**
* Session timeout in seconds.
*/
private Integer timeout;
/**
* Session tracking modes (one or more of the following: "cookie", "url", "ssl").
*/
private Set<SessionTrackingMode> trackingModes;
/**
* Persist session data between restarts.
*/
private boolean persistent;
/**
* Directory used to store session data.
*/
private File storeDir;
private Cookie cookie = new Cookie();
public Cookie getCookie() {
return this.cookie;
}
public Integer getTimeout() {
return this.timeout;
}
public void setTimeout(Integer sessionTimeout) {
this.timeout = sessionTimeout;
}
public Set<SessionTrackingMode> getTrackingModes() {
return this.trackingModes;
}
public void setTrackingModes(Set<SessionTrackingMode> trackingModes) {
this.trackingModes = trackingModes;
}
public boolean isPersistent() {
return this.persistent;
}
public void setPersistent(boolean persistent) {
this.persistent = persistent;
}
public File getStoreDir() {
return this.storeDir;
}
public void setStoreDir(File storeDir) {
this.storeDir = storeDir;
}
public static class Cookie {
/**
* Session cookie name.
*/
private String name;
/**
* Domain for the session cookie.
*/
private String domain;
/**
* Path of the session cookie.
*/
private String path;
/**
* Comment for the session cookie.
*/
private String comment;
/**
* "HttpOnly" flag for the session cookie.
*/
private Boolean httpOnly;
/**
* "Secure" flag for the session cookie.
*/
private Boolean secure;
/**
* Maximum age of the session cookie in seconds.
*/
private Integer maxAge;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDomain() {
return this.domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public String getComment() {
return this.comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Boolean getHttpOnly() {
return this.httpOnly;
}
public void setHttpOnly(Boolean httpOnly) {
this.httpOnly = httpOnly;
}
public Boolean getSecure() {
return this.secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public Integer getMaxAge() {
return this.maxAge;
}
public void setMaxAge(Integer maxAge) {
this.maxAge = maxAge;
}
}
}
public static class Tomcat {
/**
* Access log configuration.
*/
private final Accesslog accesslog = new Accesslog();
/**
* Regular expression that matches proxies that are to be trusted.
*/
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
+ "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
+ "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|"
+ "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}";
/**
* Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
*/
private String protocolHeader;
/**
* Value of the protocol header that indicates that the incoming request uses SSL.
*/
private String protocolHeaderHttpsValue = "https";
/**
* Name of the HTTP header used to override the original port value.
*/
private String portHeader = "X-Forwarded-Port";
/**
* Name of the http header from which the remote ip is extracted..
*/
private String remoteIpHeader;
/**
* Tomcat base directory. If not specified a temporary directory will be used.
*/
private File basedir;
/**
* Delay in seconds between the invocation of backgroundProcess methods.
*/
private int backgroundProcessorDelay = 30; // seconds
/**
* Maximum amount of worker threads.
*/
private int maxThreads = 0; // Number of threads in protocol handler
/**
* Minimum amount of worker threads.
*/
private int minSpareThreads = 0; // Minimum spare threads in protocol handler
/**
* Maximum size in bytes of the HTTP post content.
*/
private int maxHttpPostSize = 0; // bytes
/**
* Maximum size in bytes of the HTTP message header.
*/
private int maxHttpHeaderSize = 0; // bytes
/**
* Whether requests to the context root should be redirected by appending a / to
* the path.
*/
private Boolean redirectContextRoot;
/**
* Character encoding to use to decode the URI.
*/
private Charset uriEncoding;
/**
* Maximum number of connections that the server will accept and process at any
* given time. Once the limit has been reached, the operating system may still
* accept connections based on the "acceptCount" property.
*/
private int maxConnections = 0;
/**
* Maximum queue length for incoming connection requests when all possible request
* processing threads are in use.
*/
private int acceptCount = 0;
/**
* Comma-separated list of additional patterns that match jars to ignore for TLD
* scanning. The special '?' and '*' characters can be used in the pattern to
* match one and only one character and zero or more characters respectively.
*/
private List<String> additionalTldSkipPatterns = new ArrayList<String>();
public int getMaxThreads() {
return this.maxThreads;
}
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
public int getMinSpareThreads() {
return this.minSpareThreads;
}
public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
}
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Accesslog getAccesslog() {
return this.accesslog;
}
public int getBackgroundProcessorDelay() {
return this.backgroundProcessorDelay;
}
public void setBackgroundProcessorDelay(int backgroundProcessorDelay) {
this.backgroundProcessorDelay = backgroundProcessorDelay;
}
public File getBasedir() {
return this.basedir;
}
public void setBasedir(File basedir) {
this.basedir = basedir;
}
public String getInternalProxies() {
return this.internalProxies;
}
public void setInternalProxies(String internalProxies) {
this.internalProxies = internalProxies;
}
public String getProtocolHeader() {
return this.protocolHeader;
}
public void setProtocolHeader(String protocolHeader) {
this.protocolHeader = protocolHeader;
}
public String getProtocolHeaderHttpsValue() {
return this.protocolHeaderHttpsValue;
}
public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
}
public String getPortHeader() {
return this.portHeader;
}
public void setPortHeader(String portHeader) {
this.portHeader = portHeader;
}
public Boolean getRedirectContextRoot() {
return this.redirectContextRoot;
}
public void setRedirectContextRoot(Boolean redirectContextRoot) {
this.redirectContextRoot = redirectContextRoot;
}
public String getRemoteIpHeader() {
return this.remoteIpHeader;
}
public void setRemoteIpHeader(String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
}
public Charset getUriEncoding() {
return this.uriEncoding;
}
public void setUriEncoding(Charset uriEncoding) {
this.uriEncoding = uriEncoding;
}
public int getMaxConnections() {
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getAcceptCount() {
return this.acceptCount;
}
public void setAcceptCount(int acceptCount) {
this.acceptCount = acceptCount;
}
public List<String> getAdditionalTldSkipPatterns() {
return this.additionalTldSkipPatterns;
}
public void setAdditionalTldSkipPatterns(List<String> additionalTldSkipPatterns) {
this.additionalTldSkipPatterns = additionalTldSkipPatterns;
}
void customizeTomcat(ServerProperties serverProperties,
TomcatEmbeddedServletContainerFactory factory) {
if (getBasedir() != null) {
factory.setBaseDirectory(getBasedir());
}
factory.setBackgroundProcessorDelay(Tomcat.this.backgroundProcessorDelay);
customizeRemoteIpValve(serverProperties, factory);
if (this.maxThreads > 0) {
customizeMaxThreads(factory);
}
if (this.minSpareThreads > 0) {
customizeMinThreads(factory);
}
int maxHttpHeaderSize = (serverProperties.getMaxHttpHeaderSize() > 0
? serverProperties.getMaxHttpHeaderSize() : this.maxHttpHeaderSize);
if (maxHttpHeaderSize > 0) {
customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
}
if (this.maxHttpPostSize != 0) {
customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
}
if (this.accesslog.enabled) {
customizeAccessLog(factory);
}
if (getUriEncoding() != null) {
factory.setUriEncoding(getUriEncoding());
}
if (serverProperties.getConnectionTimeout() != null) {
customizeConnectionTimeout(factory,
serverProperties.getConnectionTimeout());
}
if (this.redirectContextRoot != null) {
customizeRedirectContextRoot(factory, this.redirectContextRoot);
}
if (this.maxConnections > 0) {
customizeMaxConnections(factory);
}
if (this.acceptCount > 0) {
customizeAcceptCount(factory);
}
if (!ObjectUtils.isEmpty(this.additionalTldSkipPatterns)) {
factory.getTldSkipPatterns().addAll(this.additionalTldSkipPatterns);
}
if (serverProperties.getError()
.getIncludeStacktrace() == ErrorProperties.IncludeStacktrace.NEVER) {
customizeErrorReportValve(factory);
}
}
private void customizeErrorReportValve(
TomcatEmbeddedServletContainerFactory factory) {
factory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
ErrorReportValve valve = new ErrorReportValve();
valve.setShowServerInfo(false);
valve.setShowReport(false);
context.getParent().getPipeline().addValve(valve);
}
});
}
private void customizeAcceptCount(TomcatEmbeddedServletContainerFactory factory) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
@SuppressWarnings("deprecation")
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
protocol.setBacklog(Tomcat.this.acceptCount);
}
}
});
}
private void customizeMaxConnections(
TomcatEmbeddedServletContainerFactory factory) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
protocol.setMaxConnections(Tomcat.this.maxConnections);
}
}
});
}
private void customizeConnectionTimeout(
TomcatEmbeddedServletContainerFactory factory,
final int connectionTimeout) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
protocol.setConnectionTimeout(connectionTimeout);
}
}
});
}
private void customizeRemoteIpValve(ServerProperties properties,
TomcatEmbeddedServletContainerFactory factory) {
String protocolHeader = getProtocolHeader();
String remoteIpHeader = getRemoteIpHeader();
// For back compatibility the valve is also enabled if protocol-header is set
if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)
|| properties.getOrDeduceUseForwardHeaders()) {
RemoteIpValve valve = new RemoteIpValve();
valve.setProtocolHeader(StringUtils.hasLength(protocolHeader)
? protocolHeader : "X-Forwarded-Proto");
if (StringUtils.hasLength(remoteIpHeader)) {
valve.setRemoteIpHeader(remoteIpHeader);
}
// The internal proxies default to a white list of "safe" internal IP
// addresses
valve.setInternalProxies(getInternalProxies());
valve.setPortHeader(getPortHeader());
valve.setProtocolHeaderHttpsValue(getProtocolHeaderHttpsValue());
// ... so it's safe to add this valve by default.
factory.addEngineValves(valve);
}
}
@SuppressWarnings("rawtypes")
private void customizeMaxThreads(TomcatEmbeddedServletContainerFactory factory) {
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);
}
}
});
}
@SuppressWarnings("rawtypes")
private void customizeMinThreads(TomcatEmbeddedServletContainerFactory factory) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractProtocol) {
AbstractProtocol protocol = (AbstractProtocol) handler;
protocol.setMinSpareThreads(Tomcat.this.minSpareThreads);
}
}
});
}
@SuppressWarnings("rawtypes")
private void customizeMaxHttpHeaderSize(
TomcatEmbeddedServletContainerFactory factory,
final int maxHttpHeaderSize) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
protocol.setMaxHttpHeaderSize(maxHttpHeaderSize);
}
}
});
}
private void customizeMaxHttpPostSize(
TomcatEmbeddedServletContainerFactory factory,
final int maxHttpPostSize) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
connector.setMaxPostSize(maxHttpPostSize);
}
});
}
private void customizeAccessLog(TomcatEmbeddedServletContainerFactory factory) {
AccessLogValve valve = new AccessLogValve();
valve.setPattern(this.accesslog.getPattern());
valve.setDirectory(this.accesslog.getDirectory());
valve.setPrefix(this.accesslog.getPrefix());
valve.setSuffix(this.accesslog.getSuffix());
valve.setRenameOnRotate(this.accesslog.isRenameOnRotate());
valve.setRequestAttributesEnabled(
this.accesslog.isRequestAttributesEnabled());
valve.setRotatable(this.accesslog.isRotate());
valve.setBuffered(this.accesslog.isBuffered());
valve.setFileDateFormat(this.accesslog.getFileDateFormat());
factory.addEngineValves(valve);
}
private void customizeRedirectContextRoot(
TomcatEmbeddedServletContainerFactory factory,
final boolean redirectContextRoot) {
factory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setMapperContextRootRedirectEnabled(redirectContextRoot);
}
});
}
public static class Accesslog {
/**
* Enable access log.
*/
private boolean enabled = false;
/**
* Format pattern for access logs.
*/
private String pattern = "common";
/**
* Directory in which log files are created. Can be relative to the tomcat
* base dir or absolute.
*/
private String directory = "logs";
/**
* Log file name prefix.
*/
protected String prefix = "access_log";
/**
* Log file name suffix.
*/
private String suffix = ".log";
/**
* Enable access log rotation.
*/
private boolean rotate = true;
/**
* Defer inclusion of the date stamp in the file name until rotate time.
*/
private boolean renameOnRotate;
/**
* Date format to place in log file name.
*/
private String fileDateFormat = ".yyyy-MM-dd";
/**
* Set request attributes for IP address, Hostname, protocol and port used for
* the request.
*/
private boolean requestAttributesEnabled;
/**
* Buffer output such that it is only flushed periodically.
*/
private boolean buffered = true;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPattern() {
return this.pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String getDirectory() {
return this.directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public boolean isRotate() {
return this.rotate;
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
}
public boolean isRenameOnRotate() {
return this.renameOnRotate;
}
public void setRenameOnRotate(boolean renameOnRotate) {
this.renameOnRotate = renameOnRotate;
}
public String getFileDateFormat() {
return this.fileDateFormat;
}
public void setFileDateFormat(String fileDateFormat) {
this.fileDateFormat = fileDateFormat;
}
public boolean isRequestAttributesEnabled() {
return this.requestAttributesEnabled;
}
public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
this.requestAttributesEnabled = requestAttributesEnabled;
}
public boolean isBuffered() {
return this.buffered;
}
public void setBuffered(boolean buffered) {
this.buffered = buffered;
}
}
}
public static class Jetty {
/**
* Maximum size in bytes of the HTTP post or put content.
*/
private int maxHttpPostSize = 0; // bytes
/**
* Number of acceptor threads to use.
*/
private Integer acceptors;
/**
* Number of selector threads to use.
*/
private Integer selectors;
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Integer getAcceptors() {
return this.acceptors;
}
public void setAcceptors(Integer acceptors) {
this.acceptors = acceptors;
}
public Integer getSelectors() {
return this.selectors;
}
public void setSelectors(Integer selectors) {
this.selectors = selectors;
}
void customizeJetty(final ServerProperties serverProperties,
JettyEmbeddedServletContainerFactory factory) {
factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders());
if (this.acceptors != null) {
factory.setAcceptors(this.acceptors);
}
if (this.selectors != null) {
factory.setSelectors(this.selectors);
}
if (serverProperties.getMaxHttpHeaderSize() > 0) {
customizeMaxHttpHeaderSize(factory,
serverProperties.getMaxHttpHeaderSize());
}
if (this.maxHttpPostSize > 0) {
customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
}
if (serverProperties.getConnectionTimeout() != null) {
customizeConnectionTimeout(factory,
serverProperties.getConnectionTimeout());
}
}
private void customizeConnectionTimeout(
JettyEmbeddedServletContainerFactory factory,
final int connectionTimeout) {
factory.addServerCustomizers(new JettyServerCustomizer() {
@Override
public void customize(Server server) {
for (org.eclipse.jetty.server.Connector connector : server
.getConnectors()) {
if (connector instanceof AbstractConnector) {
((AbstractConnector) connector)
.setIdleTimeout(connectionTimeout);
}
}
}
});
}
private void customizeMaxHttpHeaderSize(
JettyEmbeddedServletContainerFactory factory,
final int maxHttpHeaderSize) {
factory.addServerCustomizers(new JettyServerCustomizer() {
@Override
public void customize(Server server) {
for (org.eclipse.jetty.server.Connector connector : server
.getConnectors()) {
try {
for (ConnectionFactory connectionFactory : connector
.getConnectionFactories()) {
if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) {
customize(
(HttpConfiguration.ConnectionFactory) connectionFactory);
}
}
}
catch (NoSuchMethodError ex) {
customizeOnJetty8(connector, maxHttpHeaderSize);
}
}
}
private void customize(HttpConfiguration.ConnectionFactory factory) {
HttpConfiguration configuration = factory.getHttpConfiguration();
configuration.setRequestHeaderSize(maxHttpHeaderSize);
configuration.setResponseHeaderSize(maxHttpHeaderSize);
}
private void customizeOnJetty8(
org.eclipse.jetty.server.Connector connector,
int maxHttpHeaderSize) {
try {
connector.getClass().getMethod("setRequestHeaderSize", int.class)
.invoke(connector, maxHttpHeaderSize);
connector.getClass().getMethod("setResponseHeaderSize", int.class)
.invoke(connector, maxHttpHeaderSize);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
});
}
private void customizeMaxHttpPostSize(
JettyEmbeddedServletContainerFactory factory, final int maxHttpPostSize) {
factory.addServerCustomizers(new JettyServerCustomizer() {
@Override
public void customize(Server server) {
setHandlerMaxHttpPostSize(maxHttpPostSize, server.getHandlers());
}
private void setHandlerMaxHttpPostSize(int maxHttpPostSize,
Handler... handlers) {
for (Handler handler : handlers) {
if (handler instanceof ContextHandler) {
((ContextHandler) handler)
.setMaxFormContentSize(maxHttpPostSize);
}
else if (handler instanceof HandlerWrapper) {
setHandlerMaxHttpPostSize(maxHttpPostSize,
((HandlerWrapper) handler).getHandler());
}
else if (handler instanceof HandlerCollection) {
setHandlerMaxHttpPostSize(maxHttpPostSize,
((HandlerCollection) handler).getHandlers());
}
}
}
});
}
}
public static class Undertow {
/**
* Maximum size in bytes of the HTTP post content.
*/
private long maxHttpPostSize = 0; // bytes
/**
* Size of each buffer in bytes.
*/
private Integer bufferSize;
/**
* Number of buffer per region.
*/
@Deprecated
private Integer buffersPerRegion;
/**
* Number of I/O threads to create for the worker.
*/
private Integer ioThreads;
/**
* Number of worker threads.
*/
private Integer workerThreads;
/**
* Allocate buffers outside the Java heap.
*/
private Boolean directBuffers;
private final Accesslog accesslog = new Accesslog();
public long getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(long maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Integer getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(Integer bufferSize) {
this.bufferSize = bufferSize;
}
@DeprecatedConfigurationProperty(reason = "The property is not used by Undertow. See https://issues.jboss.org/browse/UNDERTOW-587 for details")
public Integer getBuffersPerRegion() {
return this.buffersPerRegion;
}
public void setBuffersPerRegion(Integer buffersPerRegion) {
this.buffersPerRegion = buffersPerRegion;
}
public Integer getIoThreads() {
return this.ioThreads;
}
public void setIoThreads(Integer ioThreads) {
this.ioThreads = ioThreads;
}
public Integer getWorkerThreads() {
return this.workerThreads;
}
public void setWorkerThreads(Integer workerThreads) {
this.workerThreads = workerThreads;
}
public Boolean getDirectBuffers() {
return this.directBuffers;
}
public void setDirectBuffers(Boolean directBuffers) {
this.directBuffers = directBuffers;
}
public Accesslog getAccesslog() {
return this.accesslog;
}
void customizeUndertow(final ServerProperties serverProperties,
UndertowEmbeddedServletContainerFactory factory) {
if (this.bufferSize != null) {
factory.setBufferSize(this.bufferSize);
}
if (this.ioThreads != null) {
factory.setIoThreads(this.ioThreads);
}
if (this.workerThreads != null) {
factory.setWorkerThreads(this.workerThreads);
}
if (this.directBuffers != null) {
factory.setDirectBuffers(this.directBuffers);
}
if (this.accesslog.enabled != null) {
factory.setAccessLogEnabled(this.accesslog.enabled);
}
factory.setAccessLogDirectory(this.accesslog.dir);
factory.setAccessLogPattern(this.accesslog.pattern);
factory.setAccessLogPrefix(this.accesslog.prefix);
factory.setAccessLogSuffix(this.accesslog.suffix);
factory.setAccessLogRotate(this.accesslog.rotate);
factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders());
if (serverProperties.getMaxHttpHeaderSize() > 0) {
customizeMaxHttpHeaderSize(factory,
serverProperties.getMaxHttpHeaderSize());
}
if (this.maxHttpPostSize > 0) {
customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
}
if (serverProperties.getConnectionTimeout() != null) {
customizeConnectionTimeout(factory,
serverProperties.getConnectionTimeout());
}
}
private void customizeConnectionTimeout(
UndertowEmbeddedServletContainerFactory factory,
final int connectionTimeout) {
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.setSocketOption(UndertowOptions.NO_REQUEST_TIMEOUT,
connectionTimeout);
}
});
}
private void customizeMaxHttpHeaderSize(
UndertowEmbeddedServletContainerFactory factory,
final int maxHttpHeaderSize) {
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.setServerOption(UndertowOptions.MAX_HEADER_SIZE,
maxHttpHeaderSize);
}
});
}
private void customizeMaxHttpPostSize(
UndertowEmbeddedServletContainerFactory factory,
final long maxHttpPostSize) {
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.setServerOption(UndertowOptions.MAX_ENTITY_SIZE,
maxHttpPostSize);
}
});
}
public static class Accesslog {
/**
* Enable access log.
*/
private Boolean enabled;
/**
* Format pattern for access logs.
*/
private String pattern = "common";
/**
* Log file name prefix.
*/
protected String prefix = "access_log.";
/**
* Log file name suffix.
*/
private String suffix = "log";
/**
* Undertow access log directory.
*/
private File dir = new File("logs");
/**
* Enable access log rotation.
*/
private boolean rotate = true;
public Boolean getEnabled() {
return this.enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getPattern() {
return this.pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public File getDir() {
return this.dir;
}
public void setDir(File dir) {
this.dir = dir;
}
public boolean isRotate() {
return this.rotate;
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
}
}
}
/**
* {@link ServletContextInitializer} to apply appropriate parts of the {@link Session}
* configuration.
*/
private static class SessionConfiguringInitializer
implements ServletContextInitializer {
private final Session session;
SessionConfiguringInitializer(Session session) {
this.session = session;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (this.session.getTrackingModes() != null) {
servletContext.setSessionTrackingModes(this.session.getTrackingModes());
}
configureSessionCookie(servletContext.getSessionCookieConfig());
}
private void configureSessionCookie(SessionCookieConfig config) {
Cookie cookie = this.session.getCookie();
if (cookie.getName() != null) {
config.setName(cookie.getName());
}
if (cookie.getDomain() != null) {
config.setDomain(cookie.getDomain());
}
if (cookie.getPath() != null) {
config.setPath(cookie.getPath());
}
if (cookie.getComment() != null) {
config.setComment(cookie.getComment());
}
if (cookie.getHttpOnly() != null) {
config.setHttpOnly(cookie.getHttpOnly());
}
if (cookie.getSecure() != null) {
config.setSecure(cookie.getSecure());
}
if (cookie.getMaxAge() != null) {
config.setMaxAge(cookie.getMaxAge());
}
}
}
}
在這個類裡我們可以找到customizeTomcat方法,大家自行看看喽,類圖如下: