diff --git a/spring-boot-containers/spring-boot-tomcat8/pom.xml b/spring-boot-containers/spring-boot-tomcat8/pom.xml
new file mode 100644
index 00000000000..e386511fc39
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-parent
+ 0.5.0.BUILD-SNAPSHOT
+ ../../spring-boot-parent
+
+ spring-boot-tomcat8
+ jar
+
+ ${basedir}/../..
+ 8.0.0-RC1
+
+
+
+
+ ${project.groupId}
+ spring-boot
+ ${project.version}
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${tomcat8.version}
+ true
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
+
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/ServletContextInitializerLifecycleListener.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/ServletContextInitializerLifecycleListener.java
new file mode 100644
index 00000000000..72c2f8d9410
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/ServletContextInitializerLifecycleListener.java
@@ -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);
+ }
+ }
+ }
+ }
+
+}
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatContextCustomizer.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatContextCustomizer.java
new file mode 100644
index 00000000000..f7a7ebc3939
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatContextCustomizer.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.tomcat8;
+
+import org.apache.catalina.Context;
+
+/**
+ * @author Dave Syer
+ */
+public interface TomcatContextCustomizer {
+
+ /**
+ * @param context the context to customize
+ */
+ void customize(Context context);
+
+}
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainer.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainer.java
new file mode 100644
index 00000000000..6b8b5a5052e
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainer.java
@@ -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;
+ }
+
+}
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainerFactory.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainerFactory.java
new file mode 100644
index 00000000000..3bb0bb0a04c
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedServletContainerFactory.java
@@ -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.
+ *
+ *
+ * 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 contextValves = new ArrayList();
+
+ private List contextLifecycleListeners = new ArrayList();
+
+ private List tomcatContextCustomizers = new ArrayList();
+
+ 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(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 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(
+ 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 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(
+ 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 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));
+ }
+
+}
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedWebappClassLoader.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedWebappClassLoader.java
new file mode 100644
index 00000000000..e091bc7cb2d
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/TomcatEmbeddedWebappClassLoader.java
@@ -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);
+ }
+ }
+ }
+
+}
diff --git a/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/package-info.java b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/package-info.java
new file mode 100644
index 00000000000..f1a98a595f8
--- /dev/null
+++ b/spring-boot-containers/spring-boot-tomcat8/src/main/java/org/springframework/boot/context/embedded/tomcat8/package-info.java
@@ -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;
+