Author: markt
Date: Mon Feb 26 10:00:12 2018
New Revision: 1825351

URL: http://svn.apache.org/viewvc?rev=1825351&view=rev
Log:
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=43866
Add additional attributes to the Manager to provide control over which 
listeners are called when an attribute is added to the session when it has 
already been added under the same name. This is to aid clustering scenarios 
where setAttribute() is often called to signal that the attribute value has 
been mutated and needs to be replicated but it may not be required, or even 
desired, for the the associated listeners to be triggered. The default 
behaviour has not been changed.

Modified:
    tomcat/trunk/java/org/apache/catalina/Manager.java
    tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
    tomcat/trunk/java/org/apache/catalina/session/StandardSession.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/manager.xml

Modified: tomcat/trunk/java/org/apache/catalina/Manager.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Manager.java?rev=1825351&r1=1825350&r2=1825351&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Manager.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Manager.java Mon Feb 26 10:00:12 2018
@@ -351,4 +351,73 @@ public interface Manager {
      *         otherwise {@code false}
      */
     public boolean willAttributeDistribute(String name, Object value);
+
+
+    /**
+     * When an attribute that is already present in the session is added again
+     * under the same name and the attribute implements {@link
+     * javax.servlet.http.HttpSessionBindingListener}, should
+     * {@link 
javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent)}
+     * be called followed by
+     * {@link 
javax.servlet.http.HttpSessionBindingListener#valueBound(javax.servlet.http.HttpSessionBindingEvent)}?
+     * <p>
+     * The default value is {@code false}.
+     *
+     * @return {@code true} if the listener will be notified, {@code false} if
+     *         it will not
+     */
+    public default boolean getNotifyBindingListenerOnUnchangedValue() {
+        return false;
+    }
+
+
+    /**
+     * Configure if
+     * {@link 
javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent)}
+     * be called followed by
+     * {@link 
javax.servlet.http.HttpSessionBindingListener#valueBound(javax.servlet.http.HttpSessionBindingEvent)}
+     * when an attribute that is already present in the session is added again
+     * under the same name and the attribute implements {@link
+     * javax.servlet.http.HttpSessionBindingListener}.
+     *
+     * @param notifyBindingListenerOnUnchangedValue {@code true} the listener
+     *                                              will be called, {@code
+     *                                              false} it will not
+     */
+    public void setNotifyBindingListenerOnUnchangedValue(
+            boolean notifyBindingListenerOnUnchangedValue);
+
+
+    /**
+     * When an attribute that is already present in the session is added again
+     * under the same name and a {@link
+     * javax.servlet.http.HttpSessionAttributeListener} is configured for the
+     * session should
+     * {@link 
javax.servlet.http.HttpSessionAttributeListener#attributeReplaced(javax.servlet.http.HttpSessionBindingEvent)}
+     * be called?
+     * <p>
+     * The default value is {@code true}.
+     *
+     * @return {@code true} if the listener will be notified, {@code false} if
+     *         it will not
+     */
+    public default boolean getNotifyAttributeListenerOnUnchangedValue() {
+        return true;
+    }
+
+
+    /**
+     * Configure if
+     * {@link 
javax.servlet.http.HttpSessionAttributeListener#attributeReplaced(javax.servlet.http.HttpSessionBindingEvent)}
+     * when an attribute that is already present in the session is added again
+     * under the same name and a {@link
+     * javax.servlet.http.HttpSessionAttributeListener} is configured for the
+     * session.
+     *
+     * @param notifyAttributeListenerOnUnchangedValue {@code true} the listener
+     *                                                will be called, {@code
+     *                                                false} it will not
+     */
+    public void setNotifyAttributeListenerOnUnchangedValue(
+            boolean notifyAttributeListenerOnUnchangedValue);
 }

Modified: tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java?rev=1825351&r1=1825350&r2=1825351&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java Mon Feb 26 
10:00:12 2018
@@ -193,6 +193,10 @@ public abstract class ManagerBase extend
 
     private boolean warnOnSessionAttributeFilterFailure;
 
+    private boolean notifyBindingListenerOnUnchangedValue;
+
+    private boolean notifyAttributeListenerOnUnchangedValue = true;
+
 
     // ------------------------------------------------------------ 
Constructors
 
@@ -209,6 +213,31 @@ public abstract class ManagerBase extend
 
     // -------------------------------------------------------------- 
Properties
 
+    @Override
+    public boolean getNotifyAttributeListenerOnUnchangedValue() {
+        return notifyAttributeListenerOnUnchangedValue;
+    }
+
+
+
+    @Override
+    public void setNotifyAttributeListenerOnUnchangedValue(boolean 
notifyAttributeListenerOnUnchangedValue) {
+        this.notifyAttributeListenerOnUnchangedValue = 
notifyAttributeListenerOnUnchangedValue;
+    }
+
+
+    @Override
+    public boolean getNotifyBindingListenerOnUnchangedValue() {
+        return notifyBindingListenerOnUnchangedValue;
+    }
+
+
+    @Override
+    public void setNotifyBindingListenerOnUnchangedValue(boolean 
notifyBindingListenerOnUnchangedValue) {
+        this.notifyBindingListenerOnUnchangedValue = 
notifyBindingListenerOnUnchangedValue;
+    }
+
+
     /**
      * Obtain the regular expression used to filter session attribute based on
      * attribute name. The regular expression is anchored so it must match the

Modified: tomcat/trunk/java/org/apache/catalina/session/StandardSession.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/StandardSession.java?rev=1825351&r1=1825350&r2=1825351&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/StandardSession.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/StandardSession.java Mon Feb 
26 10:00:12 2018
@@ -1424,8 +1424,9 @@ public class StandardSession implements
         // Call the valueBound() method if necessary
         if (notify && value instanceof HttpSessionBindingListener) {
             // Don't call any notification if replacing with the same value
+            // unless configured to do so
             Object oldValue = attributes.get(name);
-            if (value != oldValue) {
+            if (value != oldValue || 
manager.getNotifyBindingListenerOnUnchangedValue()) {
                 event = new HttpSessionBindingEvent(getSession(), name, value);
                 try {
                     ((HttpSessionBindingListener) value).valueBound(event);
@@ -1440,14 +1441,18 @@ public class StandardSession implements
         Object unbound = attributes.put(name, value);
 
         // Call the valueUnbound() method if necessary
-        if (notify && (unbound instanceof HttpSessionBindingListener) && 
(unbound != value)) {
-            try {
-                ((HttpSessionBindingListener) unbound).valueUnbound(
-                        new HttpSessionBindingEvent(getSession(), name));
-            } catch (Throwable t) {
-                ExceptionUtils.handleThrowable(t);
-                manager.getContext().getLogger().error(
-                        sm.getString("standardSession.bindingEvent"), t);
+        if (notify && unbound instanceof HttpSessionBindingListener) {
+            // Don't call any notification if replacing with the same value
+            // unless configured to do so
+            if (unbound != value || 
manager.getNotifyBindingListenerOnUnchangedValue()) {
+                try {
+                    ((HttpSessionBindingListener) unbound).valueUnbound
+                        (new HttpSessionBindingEvent(getSession(), name));
+                } catch (Throwable t) {
+                    ExceptionUtils.handleThrowable(t);
+                    manager.getContext().getLogger().error
+                        (sm.getString("standardSession.bindingEvent"), t);
+                }
             }
         }
 
@@ -1468,12 +1473,14 @@ public class StandardSession implements
             HttpSessionAttributeListener listener = 
(HttpSessionAttributeListener) listeners[i];
             try {
                 if (unbound != null) {
-                    
context.fireContainerEvent("beforeSessionAttributeReplaced", listener);
-                    if (event == null) {
-                        event = new HttpSessionBindingEvent(getSession(), 
name, unbound);
+                    if (unbound != value || 
manager.getNotifyAttributeListenerOnUnchangedValue()) {
+                        
context.fireContainerEvent("beforeSessionAttributeReplaced", listener);
+                        if (event == null) {
+                            event = new HttpSessionBindingEvent(getSession(), 
name, unbound);
+                        }
+                        listener.attributeReplaced(event);
+                        
context.fireContainerEvent("afterSessionAttributeReplaced", listener);
                     }
-                    listener.attributeReplaced(event);
-                    
context.fireContainerEvent("afterSessionAttributeReplaced", listener);
                 } else {
                     context.fireContainerEvent("beforeSessionAttributeAdded", 
listener);
                     if (event == null) {
@@ -1486,7 +1493,10 @@ public class StandardSession implements
                 ExceptionUtils.handleThrowable(t);
                 try {
                     if (unbound != null) {
-                        
context.fireContainerEvent("afterSessionAttributeReplaced", listener);
+                        if (unbound != value ||
+                                
manager.getNotifyAttributeListenerOnUnchangedValue()) {
+                            
context.fireContainerEvent("afterSessionAttributeReplaced", listener);
+                        }
                     } else {
                         
context.fireContainerEvent("afterSessionAttributeAdded", listener);
                     }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1825351&r1=1825350&r2=1825351&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Mon Feb 26 10:00:12 2018
@@ -48,6 +48,16 @@
   <subsection name="Catalina">
     <changelog>
       <fix>
+        <bug>43866</bug>: Add additional attributes to the Manager to provide
+        control over which listeners are called when an attribute is added to
+        the session when it has already been added under the same name. This is
+        to aid clustering scenarios where <code>setAttribute()</code> is often
+        called to signal that the attribute value has been mutated and needs to
+        be replicated but it may not be required, or even desired, for the the
+        associated listeners to be triggered. The default behaviour has not 
been
+        changed. (markt)
+      </fix>
+      <fix>
         Minor optimization when calling class transformers. (rjung)
       </fix>
       <add>

Modified: tomcat/trunk/webapps/docs/config/manager.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/manager.xml?rev=1825351&r1=1825350&r2=1825351&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/manager.xml (original)
+++ tomcat/trunk/webapps/docs/config/manager.xml Mon Feb 26 10:00:12 2018
@@ -73,6 +73,23 @@
         (e.g. with <code>HttpServletRequest.getSession()</code> call)
         will fail with an <code>IllegalStateException</code>.</p>
       </attribute>
+
+      <attribute name="notifyAttributeListenerOnUnchangedValue" 
required="false">
+        <p>If an attribute is added to the session and that attribute is 
already
+        present in the session under the same name will any
+        <code>HttpSessionAttributeListener</code> be notified that the 
attribute
+        has been replaced. If not specified, the default value of
+        <code>true</code> will be used.</p>
+      </attribute>
+
+      <attribute name="notifyBindingListenerOnUnchangedValue" required="false">
+        <p>If an attribute is added to the session, that attribute is already
+        present in the session under the same name and the attribute implements
+        <code>HttpSessionBindingListener</code>, will the listener be notified
+        that the attribute has been unbound and bound again. If not specified,
+        the default value of <code>false</code> will be used.</p>
+      </attribute>
+
     </attributes>
 
   </subsection>



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

Reply via email to