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 whether a listener receives notifs emitted before the
  28  * listener is registered.
  29  * @author Shanliang JIANG
  30  * @modules java.management.rmi
  31  * @run clean UnexpectedNotifTest
  32  * @run build UnexpectedNotifTest
  33  * @run main UnexpectedNotifTest
  34  */
  35 
  36 import java.util.ArrayList;
  37 import java.util.Collections;
  38 import java.util.List;
  39 import java.util.Map;
  40 import javax.management.MBeanNotificationInfo;
  41 import javax.management.MBeanServer;
  42 import javax.management.MBeanServerConnection;
  43 import javax.management.MBeanServerFactory;
  44 import javax.management.Notification;
  45 import javax.management.NotificationBroadcasterSupport;
  46 import javax.management.NotificationListener;
  47 import javax.management.ObjectName;
  48 import javax.management.remote.JMXConnector;
  49 import javax.management.remote.JMXConnectorFactory;
  50 import javax.management.remote.JMXConnectorServer;
  51 import javax.management.remote.JMXConnectorServerFactory;
  52 import javax.management.remote.JMXServiceURL;
  53 //
  54 import javax.management.remote.rmi.RMIConnectorServer;
  55 
  56 public class UnexpectedNotifTest {
  57 
  58     public static void main(String[] args) throws Exception {
  59         List<String> protos = new ArrayList<String>();
  60         protos.add("rmi");
  61         try {
  62             Class.forName("javax.management.remote.jmxmp.JMXMPConnectorServer");
  63             protos.add("jmxmp");
  64         } catch (ClassNotFoundException e) {
  65             // OK: JMXMP not present so don't test it.
  66         }
  67         for (String proto : protos)
  68             test(proto);
  69     }
  70 
  71     private static void test(String proto) throws Exception {
  72         System.out.println("Unexpected notifications test for protocol " +
  73                            proto);
  74         MBeanServer mbs = null;
  75         try {
  76             // Create a MBeanServer
  77             //
  78             mbs = MBeanServerFactory.createMBeanServer();
  79 
  80             // Create a NotificationEmitter MBean
  81             //
  82             mbean = new ObjectName ("Default:name=NotificationEmitter");
  83             mbs.registerMBean(new NotificationEmitter(), mbean);
  84 
  85             // Create a connector server
  86             //
  87             url = new JMXServiceURL("service:jmx:" + proto + "://");
  88 
  89             server = JMXConnectorServerFactory.newJMXConnectorServer(url,
  90                                                                      null,
  91                                                                      mbs);
  92 
  93             mbs.registerMBean(
  94                         server,
  95                         new ObjectName("Default:name=ConnectorServer"));
  96 
  97             server.start();
  98 
  99             url = server.getAddress();
 100 
 101             for (int j = 0; j < 2; j++) {
 102                 test();
 103             }
 104         } finally {
 105             // Stop server
 106             //
 107             server.stop();
 108             // Release the MBeanServer
 109             //
 110             MBeanServerFactory.releaseMBeanServer(mbs);
 111         }
 112     }
 113 
 114     private static void test() throws Exception {
 115         // Create client
 116         //
 117         JMXConnector connector = JMXConnectorFactory.connect(url);
 118         MBeanServerConnection client = connector.getMBeanServerConnection();
 119 
 120         // Add listener at the client side
 121         //
 122         client.addNotificationListener(mbean, listener, null, null);
 123 
 124         // Cleanup
 125         //
 126         receivedNotifs = 0;
 127 
 128         // Ask to send notifs
 129         //
 130         Object[] params = new Object[] {new Integer(nb)};
 131         String[] signatures = new String[] {"java.lang.Integer"};
 132 
 133         client.invoke(mbean, "sendNotifications", params, signatures);
 134 
 135         // Waiting...
 136         //
 137         synchronized (lock) {
 138             for (int i = 0; i < 10; i++) {
 139                 if (receivedNotifs < nb) {
 140                     lock.wait(1000);
 141                 }
 142             }
 143         }
 144 
 145         // Waiting again to ensure no more notifs
 146         //
 147         Thread.sleep(3000);
 148 
 149         synchronized (lock) {
 150             if (receivedNotifs != nb) {
 151                 throw new Exception("The client expected to receive " +
 152                                     nb + " notifs, but got " + receivedNotifs);
 153             }
 154         }
 155 
 156         // Remove listener
 157         //
 158         client.removeNotificationListener(mbean, listener);
 159 
 160         connector.close();
 161     }
 162 
 163     //--------------------------
 164     // private classes
 165     //--------------------------
 166 
 167     private static class Listener implements NotificationListener {
 168         public void handleNotification(Notification notif, Object handback) {
 169             System.out.println("Received: " + notif + " (" +
 170                                notif.getSequenceNumber() + ")");
 171             synchronized(lock) {
 172                 if(++receivedNotifs == nb) {
 173                     lock.notifyAll();
 174                 } else if (receivedNotifs > nb) {
 175                     System.out.println("The client expected to receive " +
 176                                        nb + " notifs, but got at least " +
 177                                        receivedNotifs);
 178                     System.exit(1);
 179                 }
 180             }
 181         }
 182     }
 183 
 184     public static class NotificationEmitter
 185         extends NotificationBroadcasterSupport
 186         implements NotificationEmitterMBean {
 187 
 188         /**
 189          * Returns a NotificationInfo object containing the name of the Java
 190          * class of the notification and the notification types sent by this
 191          * notification broadcaster.
 192          */
 193         public MBeanNotificationInfo[] getNotificationInfo() {
 194 
 195             MBeanNotificationInfo[] ntfInfoArray = new MBeanNotificationInfo[1];
 196 
 197             String[] ntfTypes = new String[1];
 198             ntfTypes[0] = myType;
 199 
 200             ntfInfoArray[0] = new MBeanNotificationInfo(
 201                               ntfTypes,
 202                               "javax.management.Notification",
 203                               "Notifications sent by the NotificationEmitter");
 204             return ntfInfoArray;
 205         }
 206 
 207         /**
 208          * Send a Notification object with the specified times.
 209          * The sequence number will be from zero to times-1.
 210          *
 211          * @param nb The number of notifications to send
 212          */
 213         public void sendNotifications(Integer nb) {
 214             System.out.println("NotificationEmitter: asked to send " +
 215                                "notifications: " + nb);
 216 
 217             Notification notif;
 218             for (int i = 1; i <= nb.intValue(); i++) {
 219                 notif = new Notification(myType, this, ++seqno);
 220                 sendNotification(notif);
 221             }
 222         }
 223 
 224         private String myType = "notification.my_notification";
 225     }
 226 
 227     public interface NotificationEmitterMBean {
 228         public void sendNotifications(Integer nb);
 229     }
 230 
 231     private static JMXConnectorServer server;
 232     private static JMXServiceURL url;
 233     private static ObjectName mbean;
 234     private static NotificationListener listener = new Listener();
 235 
 236     private static int nb = 10;
 237     private static int receivedNotifs = 0;
 238     private static int[] lock = new int[0];
 239     private static volatile long seqno;
 240 }