1 /*
   2  * Copyright (c) 2003, 2015, 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 7654321
  27  * @summary Tests the NotificationBuffer class.
  28  * @author Eamonn McManus
  29  * @modules java.management/com.sun.jmx.remote.internal
  30  *          java.management/com.sun.jmx.remote.util
  31  * @run clean NotificationBufferTest
  32  * @run build NotificationBufferTest NotificationSender NotificationSenderMBean
  33  * @run main NotificationBufferTest
  34  */
  35 
  36 import java.util.Arrays;
  37 import java.util.Collections;
  38 import java.util.HashSet;
  39 import java.util.List;
  40 import java.util.Set;
  41 import java.util.HashMap;
  42 
  43 import javax.management.MBeanServer;
  44 import javax.management.MBeanServerFactory;
  45 import javax.management.MBeanServerInvocationHandler;
  46 import javax.management.MBeanServerNotification;
  47 import javax.management.Notification;
  48 import javax.management.NotificationFilter;
  49 import javax.management.NotificationFilterSupport;
  50 import javax.management.ObjectName;
  51 import javax.management.loading.MLet;
  52 
  53 import javax.management.remote.NotificationResult;
  54 import javax.management.remote.TargetedNotification;
  55 
  56 import com.sun.jmx.remote.internal.ArrayNotificationBuffer;
  57 import com.sun.jmx.remote.internal.NotificationBufferFilter;
  58 import com.sun.jmx.remote.internal.NotificationBuffer;
  59 
  60 public class NotificationBufferTest {
  61 
  62     public static void main(String[] args) {
  63 //      System.setProperty("java.util.logging.config.file",
  64 //                         "../../../../logging.properties");
  65 //      // we are in <workspace>/build/test/JTwork/scratch
  66         try {
  67 //          java.util.logging.LogManager.getLogManager().readConfiguration();
  68             boolean ok = test();
  69             if (ok) {
  70                 System.out.println("Test completed");
  71                 return;
  72             } else {
  73                 System.out.println("Test failed!");
  74                 System.exit(1);
  75             }
  76         } catch (Exception e) {
  77             System.err.println("Unexpected exception: " + e);
  78             e.printStackTrace();
  79             System.exit(1);
  80         }
  81     }
  82 
  83     private static boolean test() throws Exception {
  84         MBeanServer mbs = MBeanServerFactory.createMBeanServer();
  85 
  86         Integer queuesize = new Integer(10);
  87         HashMap env = new HashMap();
  88         env.put(com.sun.jmx.remote.util.EnvHelp.BUFFER_SIZE_PROPERTY, queuesize);
  89         final NotificationBuffer nb =
  90             ArrayNotificationBuffer.getNotificationBuffer(mbs, env);
  91 
  92         final ObjectName senderName = new ObjectName("dom:type=sender");
  93         final ObjectName wildcardName = new ObjectName("*:*");
  94         final String notifType =
  95             MBeanServerNotification.REGISTRATION_NOTIFICATION;
  96 
  97         Integer allListenerId = new Integer(99);
  98         NotificationBufferFilter allListenerFilter =
  99                 makeFilter(allListenerId, wildcardName, null);
 100         NotificationFilterSupport regFilter = new NotificationFilterSupport();
 101         regFilter.enableType(notifType);
 102 
 103         // Get initial sequence number
 104         NotificationResult nr =
 105             nb.fetchNotifications(allListenerFilter, 0, 0L, 0);
 106         int nnotifs = nr.getTargetedNotifications().length;
 107         if (nnotifs > 0) {
 108             System.out.println("Expected 0 notifs for initial fetch, " +
 109                                "got " + nnotifs);
 110             return false;
 111         }
 112         System.out.println("Got 0 notifs for initial fetch, OK");
 113 
 114         long earliest = nr.getEarliestSequenceNumber();
 115         long next = nr.getNextSequenceNumber();
 116         if (earliest != next) {
 117             System.out.println("Expected earliest==next in initial fetch, " +
 118                                "earliest=" + earliest + "; next=" + next);
 119             return false;
 120         }
 121         System.out.println("Got earliest==next in initial fetch, OK");
 122 
 123         mbs.createMBean(MLet.class.getName(), null);
 124         mbs.createMBean(NotificationSender.class.getName(), senderName);
 125 
 126         NotificationSenderMBean sender = (NotificationSenderMBean)
 127             MBeanServerInvocationHandler.newProxyInstance(mbs,
 128                                                           senderName,
 129                                                           NotificationSenderMBean.class,
 130                                                           false);
 131 
 132         /* We test here that MBeans already present when the
 133            NotificationBuffer was created get a listener for the
 134            buffer, as do MBeans created later.  The
 135            MBeanServerDelegate was already present, while the
 136            NotificationSender was created later.  */
 137 
 138         // Check that the NotificationSender does indeed have a listener
 139         /* Note we are dependent on the specifics of our JMX
 140            implementation here.  There is no guarantee that the MBean
 141            creation listeners will have run to completion when
 142            creation of the MBean returns.  */
 143         int nlisteners = sender.getListenerCount();
 144         if (nlisteners != 1) {
 145             System.out.println("Notification sender should have 1 listener, " +
 146                                "has " + nlisteners);
 147             return false;
 148         }
 149         System.out.println("Notification sender has 1 listener, OK");
 150 
 151         // Now we should see two creation notifications
 152         nr = nb.fetchNotifications(allListenerFilter, next, 0L,
 153                                    Integer.MAX_VALUE);
 154         TargetedNotification[] tns = nr.getTargetedNotifications();
 155         if (tns.length != 2) {
 156             System.out.println("Expected 2 notifs, got: " +
 157                                Arrays.asList(tns));
 158             return false;
 159         }
 160         if (!(tns[0].getNotification() instanceof MBeanServerNotification)
 161             || !(tns[1].getNotification() instanceof MBeanServerNotification))
 162             {
 163             System.out.println("Expected 2 MBeanServerNotifications, got: " +
 164                                Arrays.asList(tns));
 165             return false;
 166         }
 167         if (!tns[0].getListenerID().equals(tns[1].getListenerID())
 168             || !tns[0].getListenerID().equals(allListenerId)) {
 169             System.out.println("Bad listener IDs: " + Arrays.asList(tns));
 170             return false;
 171         }
 172         System.out.println("Got 2 different MBeanServerNotifications, OK");
 173 
 174         // If we ask for max 1 notifs, we should only get one
 175         nr = nb.fetchNotifications(allListenerFilter, next, 0L, 1);
 176         tns = nr.getTargetedNotifications();
 177         if (tns.length != 1) {
 178             System.out.println("Expected 1 notif, got: " + Arrays.asList(tns));
 179             return false;
 180         }
 181         TargetedNotification tn1 = tns[0];
 182         System.out.println("Got 1 notif when asked for 1, OK");
 183 
 184         // Now we should get the other one
 185         nr = nb.fetchNotifications(allListenerFilter, nr.getNextSequenceNumber(),
 186                                    0L, 1);
 187         tns = nr.getTargetedNotifications();
 188         if (tns.length != 1) {
 189             System.out.println("Expected 1 notif, got: " + Arrays.asList(tns));
 190             return false;
 191         }
 192         TargetedNotification tn2 = tns[0];
 193         System.out.println("Got 1 notif when asked for 1 again, OK");
 194 
 195         if (tn1.getNotification() == tn2.getNotification()) {
 196             System.out.println("Returned same notif twice: " + tn1);
 197             return false;
 198         }
 199         System.out.println("2 creation notifs are different, OK");
 200 
 201         // Now we should get none (timeout is 0)
 202         long oldNext = nr.getNextSequenceNumber();
 203         nr = nb.fetchNotifications(allListenerFilter, oldNext, 0L,
 204                                    Integer.MAX_VALUE);
 205         tns = nr.getTargetedNotifications();
 206         if (tns.length != 0) {
 207             System.out.println("Expected 0 notifs, got: " +
 208                                Arrays.asList(tns));
 209             return false;
 210         }
 211         System.out.println("Got 0 notifs with 0 timeout, OK");
 212         if (nr.getNextSequenceNumber() != oldNext) {
 213             System.out.println("Sequence number changed: " + oldNext + " -> " +
 214                                nr.getNextSequenceNumber());
 215             return false;
 216         }
 217         System.out.println("Next seqno unchanged with 0 timeout, OK");
 218 
 219         // Check that timeouts work
 220         long startTime = System.currentTimeMillis();
 221         nr = nb.fetchNotifications(allListenerFilter, oldNext, 250L,
 222                                    Integer.MAX_VALUE);
 223         tns = nr.getTargetedNotifications();
 224         if (tns.length != 0) {
 225             System.out.println("Expected 0 notifs, got: " +
 226                                Arrays.asList(tns));
 227             return false;
 228         }
 229         long endTime = System.currentTimeMillis();
 230         long elapsed = endTime - startTime;
 231         if (elapsed < 250L) {
 232             System.out.println("Elapsed time shorter than timeout: " +
 233                                elapsed);
 234             return false;
 235         }
 236         System.out.println("Timeout worked, OK");
 237 
 238         // Check that notification filtering works
 239         NotificationFilter senderFilter = new NotificationFilter() {
 240             public boolean isNotificationEnabled(Notification n) {
 241                 if (!(n instanceof MBeanServerNotification))
 242                     return false;
 243                 MBeanServerNotification mbsn = (MBeanServerNotification) n;
 244                 return (mbsn.getMBeanName().equals(senderName));
 245             }
 246         };
 247         Integer senderListenerId = new Integer(88);
 248         NotificationBufferFilter senderListenerFilter =
 249                 makeFilter(senderListenerId, wildcardName, senderFilter);
 250         nr = nb.fetchNotifications(senderListenerFilter, 0, 1000L,
 251                                    Integer.MAX_VALUE);
 252         tns = nr.getTargetedNotifications();
 253         if (tns.length != 1) {
 254             System.out.println("Expected 1 notif, got: " + Arrays.asList(tns));
 255             return false;
 256         }
 257         MBeanServerNotification mbsn =
 258             (MBeanServerNotification) tns[0].getNotification();
 259         if (!mbsn.getMBeanName().equals(senderName)) {
 260             System.out.println("Expected notif with senderName, got: " +
 261                                mbsn + " (" + mbsn.getMBeanName() + ")");
 262             return false;
 263         }
 264         System.out.println("Successfully applied NotificationFilter, OK");
 265 
 266         // Now send 8 notifs to fill up our 10-element buffer
 267         sender.sendNotifs("tiddly.pom", 8);
 268         nr = nb.fetchNotifications(allListenerFilter, 0, 1000L,
 269                                    Integer.MAX_VALUE);
 270         tns = nr.getTargetedNotifications();
 271         if (tns.length != 10) {
 272             System.out.println("Expected 10 notifs, got: " +
 273                                Arrays.asList(tns));
 274             return false;
 275         }
 276         System.out.println("Got full buffer of 10 notifications, OK");
 277 
 278         // Check that the 10 notifs are the ones we expected
 279         for (int i = 0; i < 10; i++) {
 280             String expected =
 281                 (i < 2) ? notifType : "tiddly.pom";
 282             String found = tns[i].getNotification().getType();
 283             if (!found.equals(expected)) {
 284                 System.out.println("Notif " + i + " bad type: expected <" +
 285                                    expected + ">, found <" + found + ">");
 286                 return false;
 287             }
 288         }
 289         System.out.println("Notifs have right types, OK");
 290 
 291         // Check that ObjectName filtering works
 292         NotificationBufferFilter senderNameFilter =
 293                 makeFilter(new Integer(66), senderName, null);
 294         nr = nb.fetchNotifications(senderNameFilter, 0, 0L,
 295                                    Integer.MAX_VALUE);
 296         tns = nr.getTargetedNotifications();
 297         if (tns.length != 8) {
 298             System.out.println("Bad result from ObjectName filtering: " +
 299                                Arrays.asList(tns));
 300             return false;
 301         }
 302         System.out.println("ObjectName filtering works, OK");
 303 
 304         // Send one more notif, which should cause the oldest one to drop
 305         sender.sendNotifs("foo.bar", 1);
 306         nr = nb.fetchNotifications(allListenerFilter, 0, 1000L,
 307                                    Integer.MAX_VALUE);
 308         if (nr.getEarliestSequenceNumber() <= earliest) {
 309             System.out.println("Expected earliest to increase: " +
 310                                nr.getEarliestSequenceNumber() + " should be > "
 311                                + earliest);
 312             return false;
 313         }
 314         System.out.println("Earliest notif dropped, OK");
 315 
 316         // Check that the 10 notifs are the ones we expected
 317         tns = nr.getTargetedNotifications();
 318         for (int i = 0; i < 10; i++) {
 319             String expected =
 320                 (i < 1) ? notifType
 321                         : (i < 9) ? "tiddly.pom" : "foo.bar";
 322             String found = tns[i].getNotification().getType();
 323             if (!found.equals(expected)) {
 324                 System.out.println("Notif " + i + " bad type: expected <" +
 325                                    expected + ">, found <" + found + ">");
 326                 return false;
 327             }
 328         }
 329         System.out.println("Notifs have right types, OK");
 330 
 331         // Apply a filter that only selects the first notif, with max notifs 1,
 332         // then check that it skipped past the others even though it already
 333         // had its 1 notif
 334         NotificationBufferFilter firstFilter =
 335                 makeFilter(new Integer(55), wildcardName, regFilter);
 336         nr = nb.fetchNotifications(firstFilter, 0, 1000L, 1);
 337         tns = nr.getTargetedNotifications();
 338         if (tns.length != 1
 339             || !tns[0].getNotification().getType().equals(notifType)) {
 340             System.out.println("Unexpected return from filtered call: " +
 341                                Arrays.asList(tns));
 342             return false;
 343         }
 344         nr = nb.fetchNotifications(allListenerFilter, nr.getNextSequenceNumber(),
 345                                    0L, 1000);
 346         tns = nr.getTargetedNotifications();
 347         if (tns.length != 0) {
 348             System.out.println("Expected 0 notifs, got: " +
 349                                Arrays.asList(tns));
 350             return false;
 351         }
 352 
 353         // Create a second, larger buffer, which should share the same notifs
 354         nr = nb.fetchNotifications(allListenerFilter, 0,
 355                                    1000L, Integer.MAX_VALUE);
 356         queuesize = new Integer(20);
 357         env.put(com.sun.jmx.remote.util.EnvHelp.BUFFER_SIZE_PROPERTY, queuesize);
 358         NotificationBuffer nb2 =
 359             ArrayNotificationBuffer.getNotificationBuffer(mbs, env);
 360         NotificationResult nr2 =
 361             nb2.fetchNotifications(allListenerFilter, 0,
 362                                    1000L, Integer.MAX_VALUE);
 363         if (nr.getEarliestSequenceNumber() != nr2.getEarliestSequenceNumber()
 364             || nr.getNextSequenceNumber() != nr2.getNextSequenceNumber()
 365             || !sameTargetedNotifs(nr.getTargetedNotifications(),
 366                                    nr2.getTargetedNotifications()))
 367             return false;
 368         System.out.println("Adding second buffer preserved notif list, OK");
 369 
 370         // Check that the capacity is now 20
 371         sender.sendNotifs("propter.hoc", 10);
 372         nr2 = nb2.fetchNotifications(allListenerFilter, 0,
 373                                      1000L, Integer.MAX_VALUE);
 374         if (nr.getEarliestSequenceNumber() !=
 375             nr2.getEarliestSequenceNumber()) {
 376             System.out.println("Earliest seq number changed after notifs " +
 377                                "that should have fit");
 378             return false;
 379         }
 380         TargetedNotification[] tns2 = new TargetedNotification[10];
 381         Arrays.asList(nr2.getTargetedNotifications()).subList(0, 10).toArray(tns2);
 382         if (!sameTargetedNotifs(nr.getTargetedNotifications(), tns2)) {
 383             System.out.println("Early notifs changed after notifs " +
 384                                "that should have fit");
 385             return false;
 386         }
 387         System.out.println("New notifications fit in now-larger buffer, OK");
 388 
 389         // Drop the second buffer and check that the capacity shrinks
 390         nb2.dispose();
 391         NotificationResult nr3 =
 392             nb.fetchNotifications(allListenerFilter, 0,
 393                                   1000L, Integer.MAX_VALUE);
 394         if (nr3.getEarliestSequenceNumber() != nr.getNextSequenceNumber()) {
 395             System.out.println("After shrink, notifs not dropped as expected");
 396             return false;
 397         }
 398         if (nr3.getNextSequenceNumber() != nr2.getNextSequenceNumber()) {
 399             System.out.println("After shrink, next seq no does not match");
 400             return false;
 401         }
 402         tns2 = new TargetedNotification[10];
 403         Arrays.asList(nr2.getTargetedNotifications()).subList(10, 20).toArray(tns2);
 404         if (!sameTargetedNotifs(nr3.getTargetedNotifications(), tns2)) {
 405             System.out.println("Later notifs not preserved after shrink");
 406             return false;
 407         }
 408         System.out.println("Dropping second buffer shrank capacity, OK");
 409 
 410         // Final test: check that destroying the final shared buffer
 411         // removes its listeners
 412         nb.dispose();
 413         nlisteners = sender.getListenerCount();
 414         if (nlisteners != 0) {
 415             System.out.println("Disposing buffer should leave 0 listeners, " +
 416                                "but notification sender has " + nlisteners);
 417             return false;
 418         }
 419         System.out.println("Dropping first buffer drops listeners, OK");
 420 
 421         return true;
 422     }
 423 
 424     private static boolean sameTargetedNotifs(TargetedNotification[] tn1,
 425                                               TargetedNotification[] tn2) {
 426         if (tn1.length != tn2.length) {
 427             System.out.println("Not same length");
 428             return false;
 429         }
 430         for (int i = 0; i < tn1.length; i++) {
 431             TargetedNotification n1 = tn1[i];
 432             TargetedNotification n2 = tn2[i];
 433             if (n1.getNotification() != n2.getNotification()
 434                 || !n1.getListenerID().equals(n2.getListenerID()))
 435                 return false;
 436         }
 437         return true;
 438     }
 439 
 440     private static NotificationBufferFilter makeFilter(final Integer id,
 441                                                        final ObjectName pattern,
 442                                                        final NotificationFilter filter) {
 443         return new NotificationBufferFilter() {
 444             public void apply(List<TargetedNotification> notifs,
 445                               ObjectName source, Notification notif) {
 446                 if (pattern.apply(source)) {
 447                     if (filter == null || filter.isNotificationEnabled(notif))
 448                         notifs.add(new TargetedNotification(notif, id));
 449                 }
 450             }
 451         };
 452     };
 453 }