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 3c5fdd5b6941ac01c793c06ab0e4222875a4bddc Author: opencode <[email protected]> AuthorDate: Thu Oct 15 10:30:56 2026 +0200 Use unique increasing sequence numbers for MBean notifications BaseModelMBean emitted every JMX notification with the hard-coded sequence number 1, which prevents listeners that use the sequence number for ordering or to detect missed or duplicated notifications from doing so, contrary to the JMX requirements for notification sources. Add a per-MBean sequence number counter (AtomicLong since notifications may be emitted from several admin threads) and use it at both notification construction sites so attribute change notifications and generic notifications share one increasing sequence. Notifications built by callers and passed to the sendNotification/sendAttributeChangeNotification overloads keep the sequence number assigned by their constructing sender as per the JMX contract. Add a unit test asserting the exact sequence numbers of two attribute change notifications and a following generic notification. --- .../apache/tomcat/util/modeler/BaseModelMBean.java | 15 ++++++++++++--- .../modeler/TestBaseModelMBeanNotifications.java | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java index a3fa451082..6c862c9b76 100644 --- a/java/org/apache/tomcat/util/modeler/BaseModelMBean.java +++ b/java/org/apache/tomcat/util/modeler/BaseModelMBean.java @@ -19,6 +19,7 @@ package org.apache.tomcat.util.modeler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicLong; import javax.management.Attribute; import javax.management.AttributeChangeNotification; @@ -139,6 +140,11 @@ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBe */ protected String resourceType = null; + /** + * The sequence number for the next notification sent by this MBean. + */ + private final AtomicLong sequenceNumber = new AtomicLong(); + // key: operation val: invoke method // private Hashtable invokeAttMap=new Hashtable(); @@ -568,8 +574,10 @@ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBe return; // Old and new are both null == no change } - AttributeChangeNotification notification = new AttributeChangeNotification(this, 1, System.currentTimeMillis(), - "Attribute value has changed", oldValue.getName(), type, oldValue.getValue(), newValue.getValue()); + AttributeChangeNotification notification = + new AttributeChangeNotification(this, sequenceNumber.incrementAndGet(), System.currentTimeMillis(), + "Attribute value has changed", oldValue.getName(), type, oldValue.getValue(), + newValue.getValue()); sendAttributeChangeNotification(notification); } @@ -599,7 +607,8 @@ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBe new IllegalArgumentException(sm.getString("baseModelMBean.nullMessage")), sm.getString("baseModelMBean.nullMessage")); } - Notification notification = new Notification("jmx.modelmbean.generic", this, 1, message); + Notification notification = + new Notification("jmx.modelmbean.generic", this, sequenceNumber.incrementAndGet(), message); sendNotification(notification); } diff --git a/test/org/apache/tomcat/util/modeler/TestBaseModelMBeanNotifications.java b/test/org/apache/tomcat/util/modeler/TestBaseModelMBeanNotifications.java index 0d378aa7ab..b206dc66ea 100644 --- a/test/org/apache/tomcat/util/modeler/TestBaseModelMBeanNotifications.java +++ b/test/org/apache/tomcat/util/modeler/TestBaseModelMBeanNotifications.java @@ -34,9 +34,11 @@ public class TestBaseModelMBeanNotifications { private final List<String> received = new ArrayList<>(); private final List<AttributeChangeNotification> changes = new ArrayList<>(); + private final List<Long> sequences = new ArrayList<>(); @Override public void handleNotification(Notification notification, Object handback) { + sequences.add(notification.getSequenceNumber()); String name; if (notification instanceof AttributeChangeNotification acn) { changes.add(acn); @@ -170,6 +172,24 @@ public class TestBaseModelMBeanNotifications { Assert.assertEquals("updated", change.getNewValue()); } + @Test + public void testSequenceNumbersIncrease() throws Exception { + BaseModelMBean mbean = new BaseModelMBean(); + TesterNotificationListener listener = new TesterNotificationListener(); + mbean.addAttributeChangeNotificationListener(listener, "A", "hb"); + send(mbean, "A"); + send(mbean, "A"); + Assert.assertEquals(2, listener.changes.size()); + Assert.assertEquals(1L, listener.changes.get(0).getSequenceNumber()); + Assert.assertEquals(2L, listener.changes.get(1).getSequenceNumber()); + + // General notifications share the same sequence counter + mbean.addNotificationListener(listener, null, null); + mbean.sendNotification("message"); + Assert.assertEquals(3, listener.sequences.size()); + Assert.assertEquals(3L, listener.sequences.get(2).longValue()); + } + @Test public void testNonAttributeFilterIsNotRemovedByName() throws Exception { BaseModelMBean mbean = new BaseModelMBean(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
