Commit 3a82eac2 authored by Phillip Webb's avatar Phillip Webb

Prevent early initialization of Servlets

Defer loading of Servlets (with a loadOnStartup priority) until
the `EmbeddedServletContainer.start()` method is called. This prevents
issues with the DispatcherServlet being initialized before the embedded
ApplicationContext is fully initialized.
parent 3cd4026b
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -22,7 +22,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon ...@@ -22,7 +22,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon
/** /**
* Factory interface that can be used to create {@link EmbeddedServletContainer}s. * Factory interface that can be used to create {@link EmbeddedServletContainer}s.
* Implementations are encouraged to extends * Implementations are encouraged to extend
* {@link AbstractEmbeddedServletContainerFactory} when possible. * {@link AbstractEmbeddedServletContainerFactory} when possible.
* *
* @author Phillip Webb * @author Phillip Webb
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -19,6 +19,7 @@ package org.springframework.boot.context.embedded.jetty; ...@@ -19,6 +19,7 @@ package org.springframework.boot.context.embedded.jetty;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException; import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
...@@ -84,6 +85,11 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer { ...@@ -84,6 +85,11 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
} }
try { try {
this.server.start(); this.server.start();
for (Handler handler : this.server.getHandlers()) {
if (handler instanceof JettyEmbeddedWebAppContext) {
((JettyEmbeddedWebAppContext) handler).deferredInitialize();
}
}
Connector[] connectors = this.server.getConnectors(); Connector[] connectors = this.server.getConnectors();
for (Connector connector : connectors) { for (Connector connector : connectors) {
connector.start(); connector.start();
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -96,7 +96,7 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -96,7 +96,7 @@ public class JettyEmbeddedServletContainerFactory extends
@Override @Override
public EmbeddedServletContainer getEmbeddedServletContainer( public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) { ServletContextInitializer... initializers) {
WebAppContext context = new WebAppContext(); JettyEmbeddedWebAppContext context = new JettyEmbeddedWebAppContext();
int port = (getPort() >= 0 ? getPort() : 0); int port = (getPort() >= 0 ? getPort() : 0);
Server server = new Server(new InetSocketAddress(getAddress(), port)); Server server = new Server(new InetSocketAddress(getAddress(), port));
......
/*
* Copyright 2012-2014 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.jetty;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.webapp.WebAppContext;
/**
* Jetty {@link WebAppContext} used by {@link JettyEmbeddedServletContainer} to support
* deferred initialization.
*
* @author Phillip Webb
*/
class JettyEmbeddedWebAppContext extends WebAppContext {
@Override
protected ServletHandler newServletHandler() {
return new JettyEmbeddedServletHandler();
}
public void deferredInitialize() throws Exception {
((JettyEmbeddedServletHandler) getServletHandler()).deferredInitialize();
}
private static class JettyEmbeddedServletHandler extends ServletHandler {
@Override
public void initialize() throws Exception {
}
public void deferredInitialize() throws Exception {
super.initialize();
}
}
}
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.embedded.tomcat;
import org.apache.catalina.Container;
import org.apache.catalina.core.StandardContext;
/**
* Tomcat {@link StandardContext} used by {@link TomcatEmbeddedServletContainer} to
* support deferred initialization.
*
* @author Phillip Webb
*/
class TomcatEmbeddedContext extends StandardContext {
@Override
public void loadOnStartup(Container[] children) {
}
public void deferredLoadOnStartup() {
super.loadOnStartup(findChildren());
}
}
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.tomcat; ...@@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.tomcat;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.catalina.Container;
import org.apache.catalina.Engine; import org.apache.catalina.Engine;
import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState; import org.apache.catalina.LifecycleState;
...@@ -111,6 +112,11 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer ...@@ -111,6 +112,11 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
Connector connector = this.tomcat.getConnector(); Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) { if (connector != null && this.autoStart) {
try { try {
for (Container child : this.tomcat.getHost().findChildren()) {
if (child instanceof TomcatEmbeddedContext) {
((TomcatEmbeddedContext) child).deferredLoadOnStartup();
}
}
connector.getProtocolHandler().start(); connector.getProtocolHandler().start();
this.logger.info("Tomcat started on port: " + connector.getLocalPort()); this.logger.info("Tomcat started on port: " + connector.getLocalPort());
} }
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -36,7 +36,6 @@ import org.apache.catalina.LifecycleListener; ...@@ -36,7 +36,6 @@ import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Valve; import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper; import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener; import org.apache.catalina.startup.Tomcat.FixContextListener;
...@@ -119,14 +118,11 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -119,14 +118,11 @@ public class TomcatEmbeddedServletContainerFactory extends
@Override @Override
public EmbeddedServletContainer getEmbeddedServletContainer( public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) { ServletContextInitializer... initializers) {
Connector connector;
Tomcat tomcat = new Tomcat(); Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat")); : createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath()); tomcat.setBaseDir(baseDir.getAbsolutePath());
connector = new Connector(this.protocol); Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector); tomcat.getService().addConnector(connector);
customizeConnector(connector); customizeConnector(connector);
tomcat.setConnector(connector); tomcat.setConnector(connector);
...@@ -141,7 +137,7 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -141,7 +137,7 @@ public class TomcatEmbeddedServletContainerFactory extends
protected void prepareContext(Host host, ServletContextInitializer[] initializers) { protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File docBase = getValidDocumentRoot(); File docBase = getValidDocumentRoot();
docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase")); docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
Context context = new StandardContext(); TomcatEmbeddedContext context = new TomcatEmbeddedContext();
context.setName(getContextPath()); context.setName(getContextPath());
context.setPath(getContextPath()); context.setPath(getContextPath());
context.setDocBase(docBase.getAbsolutePath()); context.setDocBase(docBase.getAbsolutePath());
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -26,6 +26,7 @@ import java.util.Arrays; ...@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.GenericServlet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
...@@ -168,6 +169,23 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -168,6 +169,23 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
assertThat(date[0], notNullValue()); assertThat(date[0], notNullValue());
} }
@Test
public void loadOnStartAfterContextIsInitialized() throws Exception {
ConfigurableEmbeddedServletContainerFactory factory = getFactory();
final InitCountingServlet servlet = new InitCountingServlet();
this.container = factory
.getEmbeddedServletContainer(new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
servletContext.addServlet("test", servlet).setLoadOnStartup(1);
}
});
assertThat(servlet.getInitCount(), equalTo(0));
this.container.start();
assertThat(servlet.getInitCount(), equalTo(1));
}
@Test @Test
public void specificPort() throws Exception { public void specificPort() throws Exception {
ConfigurableEmbeddedServletContainerFactory factory = getFactory(); ConfigurableEmbeddedServletContainerFactory factory = getFactory();
...@@ -313,4 +331,23 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -313,4 +331,23 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
bean.setName("error"); bean.setName("error");
return bean; return bean;
} }
private static class InitCountingServlet extends GenericServlet {
private int initCount;
@Override
public void init() throws ServletException {
this.initCount++;
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
}
public int getInitCount() {
return this.initCount;
}
};
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment