Skip to content

Commit d032bed

Browse files
committed
Thread-safe removal of destruction callbacks in web scopes
Closes gh-23117
1 parent 160cde2 commit d032bed

File tree

3 files changed

+27
-18
lines changed

3 files changed

+27
-18
lines changed

spring-web/src/main/java/org/springframework/web/context/ContextCleanupListener.java

Lines changed: 12 additions & 8 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-2019 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.
@@ -54,22 +54,26 @@ public void contextDestroyed(ServletContextEvent event) {
5454

5555

5656
/**
57-
* Find all ServletContext attributes which implement {@link DisposableBean}
58-
* and destroy them, removing all affected ServletContext attributes eventually.
59-
* @param sc the ServletContext to check
57+
* Find all Spring-internal ServletContext attributes which implement
58+
* {@link DisposableBean} and invoke the destroy method on them.
59+
* @param servletContext the ServletContext to check
60+
* @see DisposableBean#destroy()
6061
*/
61-
static void cleanupAttributes(ServletContext sc) {
62-
Enumeration<String> attrNames = sc.getAttributeNames();
62+
static void cleanupAttributes(ServletContext servletContext) {
63+
Enumeration<String> attrNames = servletContext.getAttributeNames();
6364
while (attrNames.hasMoreElements()) {
6465
String attrName = attrNames.nextElement();
6566
if (attrName.startsWith("org.springframework.")) {
66-
Object attrValue = sc.getAttribute(attrName);
67+
Object attrValue = servletContext.getAttribute(attrName);
6768
if (attrValue instanceof DisposableBean) {
6869
try {
6970
((DisposableBean) attrValue).destroy();
7071
}
7172
catch (Throwable ex) {
72-
logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex);
73+
if (logger.isWarnEnabled()) {
74+
logger.warn("Invocation of destroy method failed on ServletContext " +
75+
"attribute with name '" + attrName + "'", ex);
76+
}
7377
}
7478
}
7579
}

spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -188,18 +188,17 @@ public void setAttribute(String name, Object value, int scope) {
188188
public void removeAttribute(String name, int scope) {
189189
if (scope == SCOPE_REQUEST) {
190190
if (isRequestActive()) {
191-
this.request.removeAttribute(name);
192191
removeRequestDestructionCallback(name);
192+
this.request.removeAttribute(name);
193193
}
194194
}
195195
else {
196196
HttpSession session = getSession(false);
197197
if (session != null) {
198198
this.sessionAttributesToUpdate.remove(name);
199199
try {
200-
session.removeAttribute(name);
201-
// Remove any registered destruction callback as well.
202200
session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name);
201+
session.removeAttribute(name);
203202
}
204203
catch (IllegalStateException ex) {
205204
// Session invalidated - shouldn't usually happen.

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -78,8 +78,10 @@ public Object get(String name, ObjectFactory<?> objectFactory) {
7878
public Object remove(String name) {
7979
Object scopedObject = this.servletContext.getAttribute(name);
8080
if (scopedObject != null) {
81+
synchronized (this.destructionCallbacks) {
82+
this.destructionCallbacks.remove(name);
83+
}
8184
this.servletContext.removeAttribute(name);
82-
this.destructionCallbacks.remove(name);
8385
return scopedObject;
8486
}
8587
else {
@@ -89,7 +91,9 @@ public Object remove(String name) {
8991

9092
@Override
9193
public void registerDestructionCallback(String name, Runnable callback) {
92-
this.destructionCallbacks.put(name, callback);
94+
synchronized (this.destructionCallbacks) {
95+
this.destructionCallbacks.put(name, callback);
96+
}
9397
}
9498

9599
@Override
@@ -112,10 +116,12 @@ public String getConversationId() {
112116
*/
113117
@Override
114118
public void destroy() {
115-
for (Runnable runnable : this.destructionCallbacks.values()) {
116-
runnable.run();
119+
synchronized (this.destructionCallbacks) {
120+
for (Runnable runnable : this.destructionCallbacks.values()) {
121+
runnable.run();
122+
}
123+
this.destructionCallbacks.clear();
117124
}
118-
this.destructionCallbacks.clear();
119125
}
120126

121127
}

0 commit comments

Comments
 (0)