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 *
31 * @run clean UnexpectedNotifTest
32 * @run build UnexpectedNotifTest
33 * @run main UnexpectedNotifTest
34 */
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.List;
39import java.util.Map;
40import javax.management.MBeanNotificationInfo;
41import javax.management.MBeanServer;
42import javax.management.MBeanServerConnection;
43import javax.management.MBeanServerFactory;
44import javax.management.Notification;
45import javax.management.NotificationBroadcasterSupport;
46import javax.management.NotificationListener;
47import javax.management.ObjectName;
48import javax.management.remote.JMXConnector;
49import javax.management.remote.JMXConnectorFactory;
50import javax.management.remote.JMXConnectorServer;
51import javax.management.remote.JMXConnectorServerFactory;
52import javax.management.remote.JMXServiceURL;
53//
54import javax.management.remote.rmi.RMIConnectorServer;
55
56public 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}
241