Skip to content

Commit

Permalink
Initialize application context with initializer-given ServletContext
Browse files Browse the repository at this point in the history
Closes gh-22319
  • Loading branch information
jhoeller committed Sep 26, 2024
1 parent 13b49d4 commit 5326640
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -58,7 +58,7 @@ public void onStartup(ServletContext servletContext) throws ServletException {
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext, servletContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -152,7 +152,7 @@ public class ContextLoader {
* The root WebApplicationContext instance that this loader manages.
*/
@Nullable
private WebApplicationContext context;
private WebApplicationContext rootContext;

/** Actual ApplicationContextInitializer instances to apply to the context. */
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
Expand Down Expand Up @@ -205,12 +205,12 @@ public ContextLoader() {
* WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and subclasses are
* free to call the {@link #closeWebApplicationContext} method on container shutdown
* to close the application context.
* @param context the application context to manage
* @param rootContext the application context to manage
* @see #initWebApplicationContext(ServletContext)
* @see #closeWebApplicationContext(ServletContext)
*/
public ContextLoader(WebApplicationContext context) {
this.context = context;
public ContextLoader(WebApplicationContext rootContext) {
this.rootContext = rootContext;
}


Expand Down Expand Up @@ -259,10 +259,10 @@ public WebApplicationContext initWebApplicationContext(ServletContext servletCon
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
if (this.rootContext == null) {
this.rootContext = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext cwac && !cwac.isActive()) {
if (this.rootContext instanceof ConfigurableWebApplicationContext cwac && !cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
Expand All @@ -273,22 +273,22 @@ public WebApplicationContext initWebApplicationContext(ServletContext servletCon
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.rootContext);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
currentContext = this.rootContext;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
currentContextPerThread.put(ccl, this.rootContext);
}

if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}

return this.context;
return this.rootContext;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
Expand Down Expand Up @@ -506,7 +506,7 @@ protected ApplicationContext loadParentContext(ServletContext servletContext) {
public void closeWebApplicationContext(ServletContext servletContext) {
servletContext.log("Closing Spring root WebApplicationContext");
try {
if (this.context instanceof ConfigurableWebApplicationContext cwac) {
if (this.rootContext instanceof ConfigurableWebApplicationContext cwac) {
cwac.close();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand All @@ -16,9 +16,12 @@

package org.springframework.web.context;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;

import org.springframework.lang.Nullable;

/**
* Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
Expand All @@ -36,6 +39,10 @@
*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

@Nullable
private ServletContext servletContext;


/**
* Create a new {@code ContextLoaderListener} that will create a web application
* context based on the "contextClass" and "contextConfigLocation" servlet
Expand All @@ -56,6 +63,19 @@ public class ContextLoaderListener extends ContextLoader implements ServletConte
public ContextLoaderListener() {
}

/**
* Create a new {@code ContextLoaderListener} with the given application context,
* initializing it with the {@link ServletContextEvent}-provided
* {@link ServletContext} reference which is spec-restricted in terms of capabilities.
* <p>It is generally preferable to initialize the application context with a
* {@link org.springframework.web.WebApplicationInitializer#onStartup}-given reference
* which is usually fully capable.
* @see #ContextLoaderListener(WebApplicationContext, ServletContext)
*/
public ContextLoaderListener(WebApplicationContext rootContext) {
super(rootContext);
}

/**
* Create a new {@code ContextLoaderListener} with the given application context. This
* constructor is useful in Servlet initializers where instance-based registration of
Expand Down Expand Up @@ -85,12 +105,15 @@ public ContextLoaderListener() {
* WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
* application context will be closed when the {@link #contextDestroyed} lifecycle
* method is invoked on this listener.
* @param context the application context to manage
* @param rootContext the application context to manage
* @param servletContext the ServletContext to initialize with
* @since 6.2
* @see #contextInitialized(ServletContextEvent)
* @see #contextDestroyed(ServletContextEvent)
*/
public ContextLoaderListener(WebApplicationContext context) {
super(context);
public ContextLoaderListener(WebApplicationContext rootContext, ServletContext servletContext) {
super(rootContext);
this.servletContext = servletContext;
}


Expand All @@ -99,7 +122,8 @@ public ContextLoaderListener(WebApplicationContext context) {
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
ServletContext scToUse = getServletContextToUse(event);
initWebApplicationContext(scToUse);
}


Expand All @@ -108,8 +132,17 @@ public void contextInitialized(ServletContextEvent event) {
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
ServletContext scToUse = getServletContextToUse(event);
closeWebApplicationContext(scToUse);
ContextCleanupListener.cleanupAttributes(scToUse);
}

/**
* Preferably use a fully-capable local ServletContext instead of
* the spec-restricted ServletContextEvent-provided reference.
*/
private ServletContext getServletContextToUse(ServletContextEvent event) {
return (this.servletContext != null ? this.servletContext : event.getServletContext());
}

}

0 comments on commit 5326640

Please sign in to comment.