1 /* 2 * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 6338874 27 * @summary Check that notification dispatch is not linear in number of MBeans. 28 * @author Eamonn McManus 29 */ 30 31 /* 32 * The notification dispatch logic in the connector server used to be 33 * linear in the number of listeners there were on any MBean. For 34 * example, if there were 1000 MBeans, each with one listener, then 35 * every time a notification was sent it would be compared against 36 * every one of the 1000 MBeans, even though its source ObjectName was 37 * known and could not possibly match the name of 999 of the MBeans. 38 * This test checks that we no longer have linear behaviour. We do 39 * this by registering just one MBean and measuring the time it takes 40 * to send and receive a certain number of notifications from that 41 * MBean. Then we register many other MBeans, each with a listener, 42 * and we make the same measurement as before. The presence of the 43 * extra MBeans with their listeners should not impact the dispatch 44 * time significantly. If it does, the test fails. 45 * 46 * As usual with timing-sensitive tests, we could potentially get 47 * sporadic failures. We fail if the ratio of the time with many 48 * MBeans to the time with just one MBean is more than 500. With the 49 * fix in place, it is usually less than 1, presumably because some 50 * code was being interpreted during the first measurement but had 51 * been compiled by the second. 52 */ 53 54 import java.util.concurrent.Semaphore; 55 import javax.management.MBeanServer; 56 import javax.management.MBeanServerConnection; 57 import javax.management.MBeanServerFactory; 58 import javax.management.MalformedObjectNameException; 59 import javax.management.Notification; 60 import javax.management.NotificationBroadcasterSupport; 61 import javax.management.NotificationListener; 62 import javax.management.ObjectName; 63 import javax.management.remote.JMXConnector; 64 import javax.management.remote.JMXConnectorFactory; 65 import javax.management.remote.JMXConnectorServer; 66 import javax.management.remote.JMXConnectorServerFactory; 67 import javax.management.remote.JMXServiceURL; 68 69 public class ListenerScaleTest { 70 private static final int WARMUP_WITH_ONE_MBEAN = 1000; 71 private static final int NOTIFS_TO_TIME = 100; 72 private static final int EXTRA_MBEANS = 20000; 73 74 private static final ObjectName testObjectName; 75 static { 76 try { 77 testObjectName = new ObjectName("test:type=Sender,number=-1"); 78 } catch (MalformedObjectNameException e) { 79 throw new RuntimeException(e); 80 } 81 } 82 83 private static volatile int nnotifs; 84 private static volatile long startTime; 85 private static volatile long elapsed; 86 private static final Semaphore sema = new Semaphore(0); 87 88 private static final NotificationListener timingListener = 89 new NotificationListener() { 90 public void handleNotification(Notification n, Object h) { 91 if (++nnotifs == NOTIFS_TO_TIME) { 92 elapsed = System.nanoTime() - startTime; 93 sema.release(); 94 } 95 } 96 }; 97 98 private static final long timeNotif(MBeanServer mbs) { 99 try { 100 startTime = System.nanoTime(); 101 nnotifs = 0; 102 mbs.invoke(testObjectName, "send", null, null); 103 sema.acquire(); 104 return elapsed; 105 } catch (Exception e) { 106 throw new RuntimeException(e); 107 } 108 } 109 110 public static interface SenderMBean { 111 public void send(); 112 } 113 114 public static class Sender extends NotificationBroadcasterSupport 115 implements SenderMBean { 116 public void send() { 117 for (int i = 0; i < NOTIFS_TO_TIME; i++) 118 sendNotification(new Notification("type", this, 1L)); 119 } 120 } 121 122 private static final NotificationListener nullListener = 123 new NotificationListener() { 124 public void handleNotification(Notification n, Object h) {} 125 }; 126 127 public static void main(String[] args) throws Exception { 128 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 129 Sender sender = new Sender(); 130 mbs.registerMBean(sender, testObjectName); 131 JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); 132 JMXConnectorServer cs = 133 JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); 134 cs.start(); 135 JMXServiceURL addr = cs.getAddress(); 136 JMXConnector cc = JMXConnectorFactory.connect(addr); 137 try { 138 test(mbs, cs, cc); 139 } finally { 140 cc.close(); 141 cs.stop(); 142 } 143 } 144 145 private static void test(MBeanServer mbs, JMXConnectorServer cs, 146 JMXConnector cc) throws Exception { 147 MBeanServerConnection mbsc = cc.getMBeanServerConnection(); 148 mbsc.addNotificationListener(testObjectName, timingListener, null, null); 149 long singleMBeanTime = 0; 150 for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++) 151 singleMBeanTime = timeNotif(mbs); 152 if (singleMBeanTime == 0) 153 singleMBeanTime = 1; 154 System.out.println("Time with a single MBean: " + singleMBeanTime + "ns"); 155 System.out.println("Now registering " + EXTRA_MBEANS + " MBeans"); 156 for (int i = 0; i < EXTRA_MBEANS; i++) { 157 ObjectName on = new ObjectName("test:type=Sender,number=" + i); 158 mbs.registerMBean(new Sender(), on); 159 if (i % 1000 == 999) { 160 System.out.print("..." + (i+1)); 161 System.out.flush(); 162 } 163 } 164 System.out.println(); 165 System.out.println("Now registering " + EXTRA_MBEANS + " listeners"); 166 for (int i = 0; i < EXTRA_MBEANS; i++) { 167 ObjectName on = new ObjectName("test:type=Sender,number=" + i); 168 mbsc.addNotificationListener(on, nullListener, null, null); 169 if (i % 1000 == 999) { 170 System.out.print("..." + (i+1)); 171 System.out.flush(); 172 } 173 } 174 System.out.println(); 175 System.out.println("Timing a notification send now"); 176 long manyMBeansTime = timeNotif(mbs); 177 System.out.println("Time with many MBeans: " + manyMBeansTime + "ns"); 178 double ratio = (double) manyMBeansTime / singleMBeanTime; 179 if (ratio > 500.0) 180 throw new Exception("Failed: ratio=" + ratio); 181 System.out.println("Test passed: ratio=" + ratio); 182 } 183 }