Thread-safe removal of destruction callbacks in web scopes
Closes gh-23117
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
@@ -54,22 +54,26 @@ public class ContextCleanupListener implements ServletContextListener {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all ServletContext attributes which implement {@link DisposableBean}
|
* Find all Spring-internal ServletContext attributes which implement
|
||||||
* and destroy them, removing all affected ServletContext attributes eventually.
|
* {@link DisposableBean} and invoke the destroy method on them.
|
||||||
* @param sc the ServletContext to check
|
* @param servletContext the ServletContext to check
|
||||||
|
* @see DisposableBean#destroy()
|
||||||
*/
|
*/
|
||||||
static void cleanupAttributes(ServletContext sc) {
|
static void cleanupAttributes(ServletContext servletContext) {
|
||||||
Enumeration<String> attrNames = sc.getAttributeNames();
|
Enumeration<String> attrNames = servletContext.getAttributeNames();
|
||||||
while (attrNames.hasMoreElements()) {
|
while (attrNames.hasMoreElements()) {
|
||||||
String attrName = attrNames.nextElement();
|
String attrName = attrNames.nextElement();
|
||||||
if (attrName.startsWith("org.springframework.")) {
|
if (attrName.startsWith("org.springframework.")) {
|
||||||
Object attrValue = sc.getAttribute(attrName);
|
Object attrValue = servletContext.getAttribute(attrName);
|
||||||
if (attrValue instanceof DisposableBean) {
|
if (attrValue instanceof DisposableBean) {
|
||||||
try {
|
try {
|
||||||
((DisposableBean) attrValue).destroy();
|
((DisposableBean) attrValue).destroy();
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex);
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn("Invocation of destroy method failed on ServletContext " +
|
||||||
|
"attribute with name '" + attrName + "'", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
@@ -188,8 +188,8 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
|
|||||||
public void removeAttribute(String name, int scope) {
|
public void removeAttribute(String name, int scope) {
|
||||||
if (scope == SCOPE_REQUEST) {
|
if (scope == SCOPE_REQUEST) {
|
||||||
if (isRequestActive()) {
|
if (isRequestActive()) {
|
||||||
this.request.removeAttribute(name);
|
|
||||||
removeRequestDestructionCallback(name);
|
removeRequestDestructionCallback(name);
|
||||||
|
this.request.removeAttribute(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -197,9 +197,8 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
|
|||||||
if (session != null) {
|
if (session != null) {
|
||||||
this.sessionAttributesToUpdate.remove(name);
|
this.sessionAttributesToUpdate.remove(name);
|
||||||
try {
|
try {
|
||||||
session.removeAttribute(name);
|
|
||||||
// Remove any registered destruction callback as well.
|
|
||||||
session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name);
|
session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name);
|
||||||
|
session.removeAttribute(name);
|
||||||
}
|
}
|
||||||
catch (IllegalStateException ex) {
|
catch (IllegalStateException ex) {
|
||||||
// Session invalidated - shouldn't usually happen.
|
// Session invalidated - shouldn't usually happen.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
@@ -78,8 +78,10 @@ public class ServletContextScope implements Scope, DisposableBean {
|
|||||||
public Object remove(String name) {
|
public Object remove(String name) {
|
||||||
Object scopedObject = this.servletContext.getAttribute(name);
|
Object scopedObject = this.servletContext.getAttribute(name);
|
||||||
if (scopedObject != null) {
|
if (scopedObject != null) {
|
||||||
|
synchronized (this.destructionCallbacks) {
|
||||||
|
this.destructionCallbacks.remove(name);
|
||||||
|
}
|
||||||
this.servletContext.removeAttribute(name);
|
this.servletContext.removeAttribute(name);
|
||||||
this.destructionCallbacks.remove(name);
|
|
||||||
return scopedObject;
|
return scopedObject;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -89,7 +91,9 @@ public class ServletContextScope implements Scope, DisposableBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerDestructionCallback(String name, Runnable callback) {
|
public void registerDestructionCallback(String name, Runnable callback) {
|
||||||
this.destructionCallbacks.put(name, callback);
|
synchronized (this.destructionCallbacks) {
|
||||||
|
this.destructionCallbacks.put(name, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -112,10 +116,12 @@ public class ServletContextScope implements Scope, DisposableBean {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
for (Runnable runnable : this.destructionCallbacks.values()) {
|
synchronized (this.destructionCallbacks) {
|
||||||
runnable.run();
|
for (Runnable runnable : this.destructionCallbacks.values()) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
this.destructionCallbacks.clear();
|
||||||
}
|
}
|
||||||
this.destructionCallbacks.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user