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

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

commit 96653cd8829a58a66674bb28fedac87312ba25c9
Author: opencode <[email protected]>
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 0dc4abb49f..368aeaee1e 100644
--- a/java/org/apache/catalina/core/ApplicationContext.java
+++ b/java/org/apache/catalina/core/ApplicationContext.java
@@ -617,6 +617,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) {
@@ -663,17 +674,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)) {
diff --git a/java/org/apache/catalina/ha/context/ReplicatedContext.java 
b/java/org/apache/catalina/ha/context/ReplicatedContext.java
index 2dd2e06bc0..15e9caf5f8 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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to