Skip to content

Commit 38063c2

Browse files
committed
Support for embedded servlet containers
** Probably not for Spring 4 M1 ** FIXME: Restore code from bootstrap project Develop new EmbeddedWebApplicationContext (along with XML and @configuration variants) that creates and controls an embedded servlet container. Unlike the traditional approach where a ContextLoaderListener bootstraps Spring and creates a new WebApplicationContext, embedded support expects that the EmbeddedWebApplicationContext will be created directly. The EmbeddedWebApplicationContext will become aware of a ServletContext once the embedded container has been started. This will happen early, allowing regular beans to call the getServletContext() method or implement ServletContextAware. The EmbeddedServletContainerFactory and EmbeddedServletContainer interfaces provide a simple abstraction for creating, starting and stopping the container. Any embedded container that supports Servlet 3.0+ initialization can be adapted. Implementations for Tomcat and Jetty are provided. The FrameworkServlet (and hence the DispatcherServlet subclass) is now ApplicationContextAware. This allows FrameworkServlets to be defined and used as Spring beans within the EmbeddedWebApplicationContext. Issue: SPR-10381
1 parent 8dbd409 commit 38063c2

File tree

13 files changed

+369
-30
lines changed

13 files changed

+369
-30
lines changed

build.gradle

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,17 @@ project("spring-web") {
456456
optional("org.codehaus.jackson:jackson-mapper-asl:1.4.2")
457457
optional("com.fasterxml.jackson.core:jackson-databind:2.0.1")
458458
optional("taglibs:standard:1.1.2")
459-
optional("org.eclipse.jetty:jetty-servlet:8.1.5.v20120716") {
459+
optional("org.eclipse.jetty:jetty-servlet:8.1.10.v20130312") {
460460
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
461461
}
462-
optional("org.eclipse.jetty:jetty-server:8.1.5.v20120716") {
462+
optional("org.eclipse.jetty:jetty-server:8.1.10.v20130312") {
463463
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
464464
}
465+
optional("org.eclipse.jetty:jetty-webapp:8.1.10.v20130312") {
466+
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
467+
}
468+
optional("org.apache.tomcat.embed:tomcat-embed-core:7.0.37")
469+
optional("org.apache.tomcat.embed:tomcat-embed-logging-juli:7.0.37")
465470
optional("log4j:log4j:1.2.17")
466471
testCompile(project(":spring-context-support")) // for JafMediaTypeFactory
467472
testCompile("xmlunit:xmlunit:1.3")
@@ -759,6 +764,17 @@ configure(rootProject) {
759764
testCompile("javax.resource:connector-api:1.5")
760765
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
761766
testCompile("hsqldb:hsqldb:${hsqldbVersion}")
767+
testCompile("org.eclipse.jetty:jetty-servlet:8.1.10.v20130312") {
768+
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
769+
}
770+
testCompile("org.eclipse.jetty:jetty-server:8.1.10.v20130312") {
771+
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
772+
}
773+
testCompile("org.eclipse.jetty:jetty-webapp:8.1.10.v20130312") {
774+
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
775+
}
776+
testCompile("org.apache.tomcat.embed:tomcat-embed-core:7.0.37")
777+
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:7.0.37")
762778
}
763779

764780
task api(type: Javadoc) {

spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -122,7 +122,7 @@ protected Resource getResourceByPath(String path) {
122122
* ClassPathResource that explicitly expresses a context-relative path
123123
* through implementing the ContextResource interface.
124124
*/
125-
private static class ClassPathContextResource extends ClassPathResource implements ContextResource {
125+
protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
126126

127127
public ClassPathContextResource(String path, ClassLoader classLoader) {
128128
super(path, classLoader);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web;
18+
19+
import javax.servlet.ServletContext;
20+
import javax.servlet.ServletException;
21+
22+
/**
23+
* Interface used to configure a Servlet 3.0+ {@link ServletContext context}
24+
* programmatically. Unlike {@link WebApplicationInitializer}, classes that implement
25+
* this interface (and do not implement {@link WebApplicationInitializer}) will
26+
* <b>not</b> be detected by {@link SpringServletContainerInitializer} and hence will
27+
* not be automatically bootstrapped by the Servlet container.
28+
*
29+
* <p>This interface is primarily designed to allow {@link ServletContextInitializer}s to
30+
* be managed by Spring and not the Servlet container.
31+
*
32+
* <p>For configuration examples see {@link WebApplicationInitializer}.
33+
*
34+
* @author Phillip Webb
35+
* @since 4.0
36+
* @see WebApplicationInitializer
37+
*/
38+
public interface ServletContextInitializer {
39+
40+
/**
41+
* Configure the given {@link ServletContext} with any servlets, filters, listeners
42+
* context-params and attributes necessary for initialization.
43+
* @param servletContext the {@code ServletContext} to initialize
44+
* @throws ServletException if any call against the given {@code ServletContext}
45+
* throws a {@code ServletException}
46+
*/
47+
void onStartup(ServletContext servletContext) throws ServletException;
48+
49+
}

spring-web/src/main/java/org/springframework/web/WebApplicationInitializer.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package org.springframework.web;
1818

1919
import javax.servlet.ServletContext;
20-
import javax.servlet.ServletException;
2120

2221
/**
2322
* Interface to be implemented in Servlet 3.0+ environments in order to configure the
@@ -175,16 +174,6 @@
175174
* @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
176175
* @see org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
177176
*/
178-
public interface WebApplicationInitializer {
179-
180-
/**
181-
* Configure the given {@link ServletContext} with any servlets, filters, listeners
182-
* context-params and attributes necessary for initializing this web application. See
183-
* examples {@linkplain WebApplicationInitializer above}.
184-
* @param servletContext the {@code ServletContext} to initialize
185-
* @throws ServletException if any call against the given {@code ServletContext}
186-
* throws a {@code ServletException}
187-
*/
188-
void onStartup(ServletContext servletContext) throws ServletException;
177+
public interface WebApplicationInitializer extends ServletContextInitializer {
189178

190179
}

spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ protected ConfigurableEnvironment createEnvironment() {
161161
*/
162162
@Override
163163
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
164-
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
164+
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
165165
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
166166
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
167167

spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.springframework.ui.context.Theme;
2929
import org.springframework.ui.context.ThemeSource;
3030
import org.springframework.ui.context.support.UiApplicationContextUtils;
31-
import org.springframework.util.Assert;
3231
import org.springframework.util.ObjectUtils;
3332
import org.springframework.util.StringUtils;
3433
import org.springframework.web.context.ConfigurableWebApplicationContext;
@@ -153,7 +152,7 @@ protected ConfigurableEnvironment createEnvironment() {
153152
*/
154153
@Override
155154
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
156-
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
155+
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
157156
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
158157

159158
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
@@ -214,8 +213,7 @@ public void setServletConfig(ServletConfig servletConfig) {
214213
}
215214

216215
public ServletConfig getServletConfig() {
217-
throw new UnsupportedOperationException(
218-
"GenericWebApplicationContext does not support getServletConfig()");
216+
return null;
219217
}
220218

221219
public void setNamespace(String namespace) {

spring-web/src/main/java/org/springframework/web/context/support/ServletContextAwareProcessor.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
* underlying bean factory. Applications do not use this directly.
3434
*
3535
* @author Juergen Hoeller
36+
* @author Phillip Webb
3637
* @since 12.03.2004
3738
* @see org.springframework.web.context.ServletContextAware
3839
* @see org.springframework.web.context.support.XmlWebApplicationContext#postProcessBeanFactory
@@ -44,6 +45,14 @@ public class ServletContextAwareProcessor implements BeanPostProcessor {
4445
private ServletConfig servletConfig;
4546

4647

48+
/**
49+
* Create a new ServletContextAwareProcessor without an initial context or config.
50+
* When this constructor is used the {@link #getServletContext()} and/or
51+
* {@link #getServletConfig()} methods should be overriden.
52+
*/
53+
protected ServletContextAwareProcessor() {
54+
}
55+
4756
/**
4857
* Create a new ServletContextAwareProcessor for the given context.
4958
*/
@@ -64,18 +73,36 @@ public ServletContextAwareProcessor(ServletConfig servletConfig) {
6473
public ServletContextAwareProcessor(ServletContext servletContext, ServletConfig servletConfig) {
6574
this.servletContext = servletContext;
6675
this.servletConfig = servletConfig;
67-
if (servletContext == null && servletConfig != null) {
68-
this.servletContext = servletConfig.getServletContext();
76+
}
77+
78+
79+
/**
80+
* Returns the {@link ServletContext} to be injected or {@code null}. This method
81+
* can be overridden by subclasses when a context is obtained after the post-processor
82+
* has been registered.
83+
*/
84+
protected ServletContext getServletContext() {
85+
if(this.servletContext == null && getServletConfig() != null) {
86+
return getServletConfig().getServletContext();
6987
}
88+
return this.servletContext;
7089
}
7190

91+
/**
92+
* Returns the {@link ServletContext} to be injected or {@code null}. This method
93+
* can be overridden by subclasses when a context is obtained after the post-processor
94+
* has been registered.
95+
*/
96+
protected ServletConfig getServletConfig() {
97+
return this.servletConfig;
98+
}
7299

73100
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
74-
if (this.servletContext != null && bean instanceof ServletContextAware) {
75-
((ServletContextAware) bean).setServletContext(this.servletContext);
101+
if (getServletContext() != null && bean instanceof ServletContextAware) {
102+
((ServletContextAware) bean).setServletContext(getServletContext());
76103
}
77-
if (this.servletConfig != null && bean instanceof ServletConfigAware) {
78-
((ServletConfigAware) bean).setServletConfig(this.servletConfig);
104+
if (getServletConfig() != null && bean instanceof ServletConfigAware) {
105+
((ServletConfigAware) bean).setServletConfig(getServletConfig());
79106
}
80107
return bean;
81108
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.context.support;
18+
19+
import javax.servlet.ServletConfig;
20+
import javax.servlet.ServletContext;
21+
22+
import org.springframework.util.Assert;
23+
import org.springframework.web.context.ConfigurableWebApplicationContext;
24+
25+
/**
26+
* Variant of {@link ServletContextAwareProcessor} for use with a
27+
* {@link ConfigurableWebApplicationContext}. Can be used when registering the processor
28+
* can occur before the {@link ServletContext} or {@link ServletConfig} have been
29+
* initialized.
30+
*
31+
* @author Phillip Webb
32+
* @since 4.0
33+
*/
34+
public class WebApplicationContextServletContextAwareProcessor extends
35+
ServletContextAwareProcessor {
36+
37+
private final ConfigurableWebApplicationContext webApplicationContext;
38+
39+
public WebApplicationContextServletContextAwareProcessor(
40+
ConfigurableWebApplicationContext webApplicationContext) {
41+
Assert.notNull(webApplicationContext, "WebApplicationContext must not be null");
42+
this.webApplicationContext = webApplicationContext;
43+
}
44+
45+
@Override
46+
protected ServletContext getServletContext() {
47+
ServletContext servletContext = this.webApplicationContext.getServletContext();
48+
return (servletContext != null ? servletContext : super.getServletContext());
49+
}
50+
51+
@Override
52+
protected ServletConfig getServletConfig() {
53+
ServletConfig servletConfig = this.webApplicationContext.getServletConfig();
54+
return (servletConfig != null ? servletConfig : super.getServletConfig());
55+
}
56+
}

spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@
2121
import java.util.ArrayList;
2222
import java.util.Collections;
2323
import java.util.concurrent.Callable;
24+
2425
import javax.servlet.ServletContext;
2526
import javax.servlet.ServletException;
2627
import javax.servlet.http.HttpServletRequest;
2728
import javax.servlet.http.HttpServletResponse;
2829
import javax.servlet.http.HttpServletResponseWrapper;
2930

3031
import org.springframework.beans.BeanUtils;
32+
import org.springframework.beans.BeansException;
3133
import org.springframework.context.ApplicationContext;
34+
import org.springframework.context.ApplicationContextAware;
3235
import org.springframework.context.ApplicationContextException;
3336
import org.springframework.context.ApplicationContextInitializer;
3437
import org.springframework.context.ApplicationListener;
@@ -122,14 +125,15 @@
122125
* @author Sam Brannen
123126
* @author Chris Beams
124127
* @author Rossen Stoyanchev
128+
* @author Phillip Webb
125129
* @see #doService
126130
* @see #setContextClass
127131
* @see #setContextConfigLocation
128132
* @see #setContextInitializerClasses
129133
* @see #setNamespace
130134
*/
131135
@SuppressWarnings("serial")
132-
public abstract class FrameworkServlet extends HttpServletBean {
136+
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
133137

134138
/**
135139
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
@@ -190,6 +194,10 @@ public abstract class FrameworkServlet extends HttpServletBean {
190194
/** WebApplicationContext for this servlet */
191195
private WebApplicationContext webApplicationContext;
192196

197+
/** If the WebApplicationContext was injected via {@link #setApplicationContext} */
198+
private boolean webApplicationContextInjected = false;
199+
200+
193201
/** Flag used to detect whether onRefresh has already been called */
194202
private boolean refreshEventReceived = false;
195203

@@ -789,6 +797,9 @@ protected void onRefresh(ApplicationContext context) {
789797
*/
790798
@Override
791799
public void destroy() {
800+
if(this.webApplicationContextInjected) {
801+
return;
802+
}
792803
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
793804
if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
794805
((ConfigurableApplicationContext) this.webApplicationContext).close();
@@ -1055,6 +1066,32 @@ protected String getUsernameForRequest(HttpServletRequest request) {
10551066
return (userPrincipal != null ? userPrincipal.getName() : null);
10561067
}
10571068

1069+
/**
1070+
* Called by Spring via {@link ApplicationContextAware} to inject the current
1071+
* application context. This method allows FrameworkServlets to be registered as
1072+
* Spring Beans inside an existing {@link WebApplicationContext} rather than
1073+
* {@link #findWebApplicationContext() finding} a
1074+
* {@link org.springframework.web.context.ContextLoaderListener bootstrapped}
1075+
* context.
1076+
*
1077+
* <p>Primarily added to support use in
1078+
* {@link org.springframework.web.context.embedded.EmbeddedWebApplicationContext embedded
1079+
* servlet containers}, this method is not intended to be called directly.
1080+
* @since 4.0
1081+
*/
1082+
public void setApplicationContext(ApplicationContext applicationContext)
1083+
throws BeansException {
1084+
if (this.webApplicationContext == null
1085+
&& applicationContext instanceof WebApplicationContext) {
1086+
if(logger.isDebugEnabled()) {
1087+
logger.debug("Using existing application context for "
1088+
+ ClassUtils.getShortName(getClass()));
1089+
}
1090+
this.webApplicationContext = (WebApplicationContext) applicationContext;
1091+
this.webApplicationContextInjected = true;
1092+
}
1093+
}
1094+
10581095

10591096
/**
10601097
* Subclasses must implement this method to do the work of request handling,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
port=8080

0 commit comments

Comments
 (0)