1 /*
   2  * Copyright (c) 2004, 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 6199899
  27  * @summary Tests reconnection done by a fetching notif thread.
  28  * @author Shanliang JIANG
  29  * @modules java.management.rmi
  30  * @run clean NotifReconnectDeadlockTest
  31  * @run build NotifReconnectDeadlockTest
  32  * @run main NotifReconnectDeadlockTest
  33  */
  34 
  35 import java.util.HashMap;
  36 import java.util.Map;
  37 import javax.management.MBeanServer;
  38 import javax.management.MBeanServerFactory;
  39 import javax.management.Notification;
  40 import javax.management.NotificationBroadcasterSupport;
  41 import javax.management.NotificationListener;
  42 import javax.management.ObjectName;
  43 import javax.management.remote.JMXConnectionNotification;
  44 import javax.management.remote.JMXConnector;
  45 import javax.management.remote.JMXConnectorFactory;
  46 import javax.management.remote.JMXConnectorServer;
  47 import javax.management.remote.JMXConnectorServerFactory;
  48 import javax.management.remote.JMXServiceURL;
  49 
  50 /**
  51  * "This test checks for a bug whereby reconnection did not work if (a) it was
  52  * initiated by the fetchNotifications thread and (b) it succeeded. These conditions
  53  * are not usual, since connection failure is usually caused by either idle timeout
  54  * (which doesn't usually happen if fetchNotifications is running) or communication
  55  * problems (which are usually permanent so reconnection fails).  But they can happen,
  56  * so we test for them here.
  57  * The test sets a very short idle timeout, and effectively suspends the
  58  * fetchNotifications thread by having it invoke a listener with a delay in it.
  59  * This means that the idle timeout happens.  When the delayed listener returns,
  60  * the fetchNotifications thread will attempt to reconnect, and this attempt should
  61  * succeed, so we meet the two conditions above.
  62  * The test succeeds if there is indeed a reconnection, detected by the connection
  63  * listener seeing an OPENED notification.  The connection listener should not see
  64  * a CLOSED or FAILED notification."
  65  */
  66 public class NotifReconnectDeadlockTest {
  67 
  68     public static void main(String[] args) throws Exception {
  69         System.out.println(
  70            ">>> Tests reconnection done by a fetching notif thread.");
  71 
  72         ObjectName oname = new ObjectName ("Default:name=NotificationEmitter");
  73         JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
  74         Map env = new HashMap(2);
  75         env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout));
  76         env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE));
  77 
  78         final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
  79 
  80         mbs.registerMBean(new NotificationEmitter(), oname);
  81         JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
  82                                                                                url,
  83                                                                                env,
  84                                                                                mbs);
  85         server.start();
  86 
  87         JMXServiceURL addr = server.getAddress();
  88         JMXConnector client = JMXConnectorFactory.connect(addr, env);
  89 
  90         Thread.sleep(100); // let pass the first client open notif if there is
  91         client.getMBeanServerConnection().addNotificationListener(oname,
  92                                                                   listener,
  93                                                                   null,
  94                                                                   null);
  95 
  96         client.addConnectionNotificationListener(listener, null, null);
  97 
  98         // max test time: 2 minutes
  99         final long end = System.currentTimeMillis()+120000;
 100 
 101         synchronized(lock) {
 102             while(clientState == null && System.currentTimeMillis() < end) {
 103                 mbs.invoke(oname, "sendNotifications",
 104                            new Object[] {new Notification("MyType", "", 0)},
 105                            new String[] {"javax.management.Notification"});
 106 
 107                 try {
 108                     lock.wait(10);
 109                 } catch (Exception e) {}
 110             }
 111         }
 112 
 113         if (clientState == null) {
 114             throw new RuntimeException(
 115                   "No reconnection happened, need to reconfigure the test.");
 116         } else if (JMXConnectionNotification.FAILED.equals(clientState) ||
 117                    JMXConnectionNotification.CLOSED.equals(clientState)) {
 118             throw new RuntimeException("Failed to reconnect.");
 119         }
 120 
 121         System.out.println(">>> Passed!");
 122 
 123         client.removeConnectionNotificationListener(listener);
 124         client.close();
 125         server.stop();
 126     }
 127 
 128 //--------------------------
 129 // private classes
 130 //--------------------------
 131     public static class NotificationEmitter extends NotificationBroadcasterSupport
 132         implements NotificationEmitterMBean {
 133 
 134         public void sendNotifications(Notification n) {
 135             sendNotification(n);
 136         }
 137     }
 138 
 139     public interface NotificationEmitterMBean {
 140         public void sendNotifications(Notification n);
 141     }
 142 
 143     private final static NotificationListener listener = new NotificationListener() {
 144             public void handleNotification(Notification n, Object hb) {
 145 
 146                 // treat the client notif to know the end
 147                 if (n instanceof JMXConnectionNotification) {
 148                     if (!JMXConnectionNotification.NOTIFS_LOST.equals(n.getType())) {
 149 
 150                         clientState = n.getType();
 151                         System.out.println(
 152                            ">>> The client state has been changed to: "+clientState);
 153 
 154                         synchronized(lock) {
 155                             lock.notifyAll();
 156                         }
 157                     }
 158 
 159                     return;
 160                 }
 161 
 162                 System.out.println(">>> Do sleep to make reconnection.");
 163                 synchronized(lock) {
 164                     try {
 165                         lock.wait(listenerSleep);
 166                     } catch (Exception e) {
 167                         // OK
 168                     }
 169                 }
 170             }
 171         };
 172 
 173     private static final long serverTimeout = 1000;
 174     private static final long listenerSleep = serverTimeout*6;
 175 
 176     private static String clientState = null;
 177     private static final int[] lock = new int[0];
 178 }