Support for embedded Tomcat 8 container parallel to the current Tomcat 7

This commit is contained in:
Sergey Shcherbakov 2013-08-14 20:49:52 +02:00 committed by Phillip Webb
parent 322b5e5c2b
commit b4542f722f
8 changed files with 773 additions and 0 deletions

View File

@ -22,6 +22,7 @@
<module>spring-boot-starters</module>
<module>spring-boot-cli</module>
<module>spring-boot-integration-tests</module>
<module>spring-boot-containers/spring-boot-tomcat8</module>
</modules>
<build>
<plugins>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
<relativePath>../../spring-boot-parent</relativePath>
</parent>
<artifactId>spring-boot-tomcat8</artifactId>
<packaging>jar</packaging>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
<tomcat8.version>8.0.0-RC1</tomcat8.version>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-boot</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat8.version}</version>
<optional>true</optional>
</dependency>
<!-- Test -->
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-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.tomcat8;
import javax.servlet.ServletException;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.StandardContext;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.util.Assert;
/**
* Tomcat {@link LifecycleListener} that calls {@link ServletContextInitializer}s.
*
* @author Phillip Webb
*/
public class ServletContextInitializerLifecycleListener implements LifecycleListener {
private ServletContextInitializer[] initializers;
/**
* Create a new {@link ServletContextInitializerLifecycleListener} instance with the
* specified initializers.
* @param initializers the initializers to call
*/
public ServletContextInitializerLifecycleListener(
ServletContextInitializer... initializers) {
this.initializers = initializers;
}
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {
Assert.isInstanceOf(StandardContext.class, event.getSource());
StandardContext standardContext = (StandardContext) event.getSource();
for (ServletContextInitializer initializer : this.initializers) {
try {
initializer.onStartup(standardContext.getServletContext());
}
catch (ServletException ex) {
throw new IllegalStateException(ex);
}
}
}
}
}

View File

@ -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.tomcat8;
import org.apache.catalina.Context;
/**
* @author Dave Syer
*/
public interface TomcatContextCustomizer {
/**
* @param context the context to customize
*/
void customize(Context context);
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2002-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.tomcat8;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.util.Assert;
/**
* {@link EmbeddedServletContainer} that can be used to control an embedded Tomcat server.
* Usually this class should be created using the
* {@link TomcatEmbeddedServletContainerFactory} and not directly.
*
* @author Phillip Webb
* @author Dave Syer
* @see TomcatEmbeddedServletContainerFactory
*/
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
private final Log logger = LogFactory.getLog(TomcatEmbeddedServletContainer.class);
private static int containerCounter = 0;
private final Tomcat tomcat;
/**
* Create a new {@link TomcatEmbeddedServletContainer} instance.
* @param tomcat the underlying Tomcat server
*/
public TomcatEmbeddedServletContainer(Tomcat tomcat) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
initialize();
}
private synchronized void initialize() throws EmbeddedServletContainerException {
try {
this.tomcat.start();
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
Thread awaitThread = new Thread("container-" + (containerCounter++)) {
@Override
public void run() {
TomcatEmbeddedServletContainer.this.tomcat.getServer().await();
};
};
awaitThread.setDaemon(false);
awaitThread.start();
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Tomcat", ex);
}
}
@Override
public void start() throws EmbeddedServletContainerException {
Connector connector = this.tomcat.getConnector();
if (connector != null) {
try {
connector.getProtocolHandler().resume();
}
catch (Exception e) {
this.logger.error("Cannot start connector: ", e);
}
}
}
@Override
public synchronized void stop() throws EmbeddedServletContainerException {
try {
try {
this.tomcat.stop();
}
catch (LifecycleException ex) {
// swallow and continue
}
this.tomcat.destroy();
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to stop embdedded Tomcat", ex);
}
}
/**
* Returns access to the underlying Tomcat server.
*/
public Tomcat getTomcat() {
return this.tomcat;
}
}

View File

@ -0,0 +1,383 @@
/*
* Copyright 2002-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.tomcat8;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.coyote.AbstractProtocol;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.boot.context.embedded.MimeMappings;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link EmbeddedServletContainerFactory} that can be used to create
* {@link TomcatEmbeddedServletContainer}s. Can be initialized using Spring's
* {@link ServletContextInitializer}s or Tomcat {@link LifecycleListener}s.
*
* <p>
* Unless explicitly configured otherwise this factory will created containers that
* listens for HTTP requests on port 8080.
*
* @author Phillip Webb
* @author Dave Syer
* @see #setPort(int)
* @see #setContextLifecycleListeners(Collection)
* @see TomcatEmbeddedServletContainer
*/
public class TomcatEmbeddedServletContainerFactory extends
AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
private static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private File baseDirectory;
private List<Valve> contextValves = new ArrayList<Valve>();
private List<LifecycleListener> contextLifecycleListeners = new ArrayList<LifecycleListener>();
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<TomcatContextCustomizer>();
private ResourceLoader resourceLoader;
private String protocol = DEFAULT_PROTOCOL;
/**
* Create a new {@link TomcatEmbeddedServletContainerFactory} instance.
*/
public TomcatEmbeddedServletContainerFactory() {
super();
}
/**
* Create a new {@link TomcatEmbeddedServletContainerFactory} that listens for
* requests using the specified port.
* @param port the port to listen on
*/
public TomcatEmbeddedServletContainerFactory(int port) {
super(port);
}
/**
* Create a new {@link TomcatEmbeddedServletContainerFactory} with the specified
* context path and port.
* @param contextPath root the context path
* @param port the port to listen on
*/
public TomcatEmbeddedServletContainerFactory(String contextPath, int port) {
super(contextPath, port);
}
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
Connector connector;
Tomcat tomcat = new Tomcat();
if (getPort() == 0) {
return EmbeddedServletContainer.NONE;
}
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
connector = new Connector(this.protocol);
customizeConnector(connector);
tomcat.getService().addConnector(connector);
tomcat.setConnector(connector);
try {
// Allow the server to start so the ServletContext is available, but stop the
// connector to prevent requests from being handled before the Spring context
// is ready:
connector.getProtocolHandler().pause();
}
catch (Exception e) {
this.logger.error("Cannot pause connector: ", e);
}
tomcat.getHost().setAutoDeploy(false);
tomcat.getEngine().setBackgroundProcessorDelay(-1);
prepareContext(tomcat.getHost(), initializers);
return getTomcatEmbeddedServletContainer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File docBase = getValidDocumentRoot();
docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
Context context = new StandardContext();
context.setName(getContextPath());
context.setPath(getContextPath());
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader
.getClassLoader() : ClassUtils.getDefaultClassLoader());
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
context.setLoader(loader);
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (isRegisterJspServlet()
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(context);
}
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
configureContext(context, initializersToUse);
host.addChild(context);
postProcessContext(context);
}
private void addDefaultServlet(Context context) {
Wrapper defaultServlet = context.createWrapper();
defaultServlet.setName("default");
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
defaultServlet.addInitParameter("debug", "0");
defaultServlet.addInitParameter("listings", "false");
defaultServlet.setLoadOnStartup(1);
// Otherwise the default location of a Spring DispatcherServlet cannot be set
defaultServlet.setOverridable(true);
context.addChild(defaultServlet);
context.addServletMapping("/", "default");
}
private void addJspServlet(Context context) {
Wrapper jspServlet = context.createWrapper();
jspServlet.setName("jsp");
jspServlet.setServletClass(getJspServletClassName());
jspServlet.addInitParameter("fork", "false");
jspServlet.setLoadOnStartup(3);
context.addChild(jspServlet);
context.addServletMapping("*.jsp", "jsp");
context.addServletMapping("*.jspx", "jsp");
}
// Needs to be protected so it can be used by subclasses
protected void customizeConnector(Connector connector) {
connector.setPort(getPort());
if (connector.getProtocolHandler() instanceof AbstractProtocol
&& getAddress() != null) {
((AbstractProtocol) connector.getProtocolHandler()).setAddress(getAddress());
}
}
/**
* Configure the Tomcat {@link Context}.
* @param context the Tomcat context
* @param initializers initializers to apply
*/
protected void configureContext(Context context,
ServletContextInitializer[] initializers) {
context.addLifecycleListener(new ServletContextInitializerLifecycleListener(
initializers));
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
for (Valve valve : this.contextValves) {
context.getPipeline().addValve(valve);
}
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatPage.setLocation(errorPage.getPath());
tomcatPage.setExceptionType(errorPage.getExceptionName());
tomcatPage.setErrorCode(errorPage.getStatusCode());
context.addErrorPage(tomcatPage);
}
for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
context.setSessionTimeout(getSessionTimeout());
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
/**
* Post process the Tomcat {@link Context} before it used with the Tomcat Server.
* Subclasses can override this method to apply additional processing to the
* {@link Context}.
* @param context the Tomcat {@link Context}
*/
protected void postProcessContext(Context context) {
}
/**
* Factory method called to create the {@link TomcatEmbeddedServletContainer}.
* Subclasses can override this method to return a different
* {@link TomcatEmbeddedServletContainer} or apply additional processing to the Tomcat
* server.
* @param tomcat the Tomcat server.
* @return a new {@link TomcatEmbeddedServletContainer} instance
*/
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat);
}
private File createTempDir(String prefix) {
try {
File tempFolder = File.createTempFile(prefix + ".", "." + getPort());
tempFolder.delete();
tempFolder.mkdir();
tempFolder.deleteOnExit();
return tempFolder;
}
catch (IOException ex) {
throw new EmbeddedServletContainerException(
"Unable to create Tomcat tempdir", ex);
}
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* Set the Tomcat base directory. If not specified a temporary directory will be used.
* @param baseDirectory the tomcat base directory
*/
public void setBaseDirectory(File baseDirectory) {
this.baseDirectory = baseDirectory;
}
/**
* The Tomcat protocol to use when create the {@link Connector}.
* @see Connector#Connector(String)
*/
public void setProtocol(String protocol) {
Assert.hasLength(protocol, "Protocol must not be empty");
this.protocol = protocol;
}
/**
* Set {@link Valve}s that should be applied to the Tomcat {@link Context}. Calling
* this method will replace any existing listeners.
* @param contextValves the valves to set
*/
public void setContextValves(Collection<? extends Valve> contextValves) {
Assert.notNull(contextValves, "Valves must not be null");
this.contextValves = new ArrayList<Valve>(contextValves);
}
/**
* Returns a mutable collection of the {@link Valve}s that will be applied to the
* Tomcat {@link Context}.
* @return the contextValves the valves that will be applied
*/
public Collection<Valve> getValves() {
return this.contextValves;
}
/**
* Add {@link Valve}s that should be applied to the Tomcat {@link Context}.
* @param contextValves the valves to add
*/
public void addContextValves(Valve... contextValves) {
Assert.notNull(contextValves, "Valves must not be null");
this.contextValves.addAll(Arrays.asList(contextValves));
}
/**
* Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context}
* . Calling this method will replace any existing listeners.
* @param contextLifecycleListeners the listeners to set
*/
public void setContextLifecycleListeners(
Collection<? extends LifecycleListener> contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners,
"ContextLifecycleListeners must not be null");
this.contextLifecycleListeners = new ArrayList<LifecycleListener>(
contextLifecycleListeners);
}
/**
* Returns a mutable collection of the {@link LifecycleListener}s that will be applied
* to the Tomcat {@link Context} .
* @return the contextLifecycleListeners the listeners that will be applied
*/
public Collection<LifecycleListener> getContextLifecycleListeners() {
return this.contextLifecycleListeners;
}
/**
* Add {@link LifecycleListener}s that should be added to the Tomcat {@link Context}.
* @param contextLifecycleListeners the listeners to add
*/
public void addContextLifecycleListeners(
LifecycleListener... contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners,
"ContextLifecycleListeners must not be null");
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
}
/**
* Set {@link TomcatContextCustomizer}s that should be applied to the Tomcat
* {@link Context} . Calling this method will replace any existing customizers.
* @param tomcatContextCustomizers the customizers to set
*/
public void setTomcatContextCustomizers(
Collection<? extends TomcatContextCustomizer> tomcatContextCustomizers) {
Assert.notNull(this.contextLifecycleListeners,
"TomcatContextCustomizers must not be null");
this.tomcatContextCustomizers = new ArrayList<TomcatContextCustomizer>(
tomcatContextCustomizers);
}
/**
* 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<TomcatContextCustomizer> getTomcatContextCustomizers() {
return this.tomcatContextCustomizers;
}
/**
* Add {@link TomcatContextCustomizer}s that should be added to the Tomcat
* {@link Context}.
* @param tomcatContextCustomizers the customizers to add
*/
public void addContextCustomizers(TomcatContextCustomizer... tomcatContextCustomizers) {
Assert.notNull(this.tomcatContextCustomizers,
"TomcatContextCustomizer must not be null");
this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers));
}
}

View File

@ -0,0 +1,115 @@
package org.springframework.boot.context.embedded.tomcat8;
/*
* 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.
*/
import org.apache.catalina.loader.WebappClassLoader;
/**
* Extension of Tomcat's {@link WebappClassLoader} that does not consider the
* {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to to
* ensure that any custom context classloader is always used (as is the case with some
* executable archives).
*
* @author Phillip Webb
*/
public class TomcatEmbeddedWebappClassLoader extends WebappClassLoader {
public TomcatEmbeddedWebappClassLoader() {
super();
}
public TomcatEmbeddedWebappClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class<?> resultClass = null;
// Check local class caches
resultClass = (resultClass == null ? findLoadedClass0(name) : resultClass);
resultClass = (resultClass == null ? findLoadedClass(name) : resultClass);
if (resultClass != null) {
return resolveIfNecessary(resultClass, resolve);
}
// Check security
checkPackageAccess(name);
// Perform the actual load
boolean delegateLoad = (this.delegate || filter(name));
if (delegateLoad) {
resultClass = (resultClass == null ? loadFromParent(name) : resultClass);
}
resultClass = (resultClass == null ? findClassIgnoringNotFound(name)
: resultClass);
if (!delegateLoad) {
resultClass = (resultClass == null ? loadFromParent(name) : resultClass);
}
if (resultClass == null) {
throw new ClassNotFoundException(name);
}
return resolveIfNecessary(resultClass, resolve);
}
private Class<?> resolveIfNecessary(Class<?> resultClass, boolean resolve) {
if (resolve) {
resolveClass(resultClass);
}
return (resultClass);
}
private Class<?> loadFromParent(String name) {
if (this.parent == null) {
return null;
}
try {
return Class.forName(name, false, this.parent);
}
catch (ClassNotFoundException e) {
return null;
}
}
private Class<?> findClassIgnoringNotFound(String name) {
try {
return findClass(name);
}
catch (ClassNotFoundException e) {
return null;
}
}
private void checkPackageAccess(String name) throws ClassNotFoundException {
if (this.securityManager != null && name.lastIndexOf('.') >= 0) {
try {
this.securityManager.checkPackageAccess(name.substring(0,
name.lastIndexOf('.')));
}
catch (SecurityException se) {
throw new ClassNotFoundException("Security Violation, attempt to use "
+ "Restricted Class: " + name, se);
}
}
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2002-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.
*/
/**
* Support for Tomcat {@link org.springframework.boot.context.embedded.EmbeddedServletContainer EmbeddedServletContainers}.
*/
package org.springframework.boot.context.embedded.tomcat8;