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