You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2026/10/01 06:41:31 UTC

(tomcat) 08/25: Fire ServletContextAttributeListener events for attributes stored in the local map of a ReplicatedContext by extracting the event firing logic of ApplicationContext into overridable hooks

This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit be1f1d151d7221afe5ea8b2cf78d626b70f8a410
Author: opencode <re...@apache.org>
AuthorDate: Wed Sep 30 22:51:14 2026 +0200

    Fire ServletContextAttributeListener events for attributes stored in the local map of a ReplicatedContext by extracting the event firing logic of ApplicationContext into overridable hooks
---
 .../apache/catalina/core/ApplicationContext.java   | 31 +++++++++++++++++-----
 .../catalina/ha/context/ReplicatedContext.java     | 13 +++++++--
 2 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/java/org/apache/catalina/core/ApplicationContext.java b/java/org/apache/catalina/core/ApplicationContext.java
index d499bbe0ab..48a19e0bd6 100644
--- a/java/org/apache/catalina/core/ApplicationContext.java
+++ b/java/org/apache/catalina/core/ApplicationContext.java
@@ -561,6 +561,17 @@ public class ApplicationContext implements ServletContext {
             return;
         }
 
+        fireContextAttributeRemoved(name, value);
+    }
+
+
+    /**
+     * Notify the application's <code>ServletContextAttributeListener</code>s of an attribute removal.
+     *
+     * @param name  The name of the attribute
+     * @param value The value the attribute had before it was removed
+     */
+    protected void fireContextAttributeRemoved(String name, Object value) {
         // Notify interested application event listeners
         Object[] listeners = context.getApplicationEventListeners();
         if (listeners == null || listeners.length == 0) {
@@ -606,17 +617,25 @@ public class ApplicationContext implements ServletContext {
         Object oldValue = attributes.put(name, value);
         boolean replaced = oldValue != null;
 
+        fireContextAttributeAddedOrReplaced(replaced, name, replaced ? oldValue : value);
+    }
+
+
+    /**
+     * Notify the application's <code>ServletContextAttributeListener</code>s of an attribute addition or replacement.
+     *
+     * @param replaced Whether the attribute was replaced rather than added
+     * @param name     The name of the attribute
+     * @param value    The value for the event: the previous value when the attribute was replaced, otherwise the new
+     *                     value
+     */
+    protected void fireContextAttributeAddedOrReplaced(boolean replaced, String name, Object value) {
         // Notify interested application event listeners
         Object[] listeners = context.getApplicationEventListeners();
         if (listeners == null || listeners.length == 0) {
             return;
         }
-        ServletContextAttributeEvent event;
-        if (replaced) {
-            event = new ServletContextAttributeEvent(context.getServletContext(), name, oldValue);
-        } else {
-            event = new ServletContextAttributeEvent(context.getServletContext(), name, value);
-        }
+        ServletContextAttributeEvent event = new ServletContextAttributeEvent(context.getServletContext(), name, value);
 
         for (Object obj : listeners) {
             if (!(obj instanceof ServletContextAttributeListener listener)) {
diff --git a/java/org/apache/catalina/ha/context/ReplicatedContext.java b/java/org/apache/catalina/ha/context/ReplicatedContext.java
index d4c3012b33..4dda906f1a 100644
--- a/java/org/apache/catalina/ha/context/ReplicatedContext.java
+++ b/java/org/apache/catalina/ha/context/ReplicatedContext.java
@@ -235,8 +235,14 @@ public class ReplicatedContext extends StandardContext implements MapOwner {
 
         @Override
         public void removeAttribute(String name) {
-            tomcatAttributes.remove(name);
+            Object localValue = tomcatAttributes.remove(name);
+            boolean inAttributeMap = attributes.containsKey(name);
             super.removeAttribute(name);
+            if (localValue != null && !inAttributeMap) {
+                // The attribute was only stored locally, so the removal event
+                // was not fired by the attribute map
+                fireContextAttributeRemoved(name, localValue);
+            }
         }
 
         @Override
@@ -249,7 +255,10 @@ public class ReplicatedContext extends StandardContext implements MapOwner {
                 return;
             }
             if ((!getParent().getState().isAvailable()) || JSP_APP_CONTEXT_NAME.equals(name)) {
-                tomcatAttributes.put(name, value);
+                Object oldValue = tomcatAttributes.put(name, value);
+                // The attribute is stored locally, so the event has to be fired
+                // here rather than by the attribute map
+                fireContextAttributeAddedOrReplaced(oldValue != null, name, oldValue != null ? oldValue : value);
             } else {
                 super.setAttribute(name, value);
             }


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org