1 /*
   2  * Copyright (c) 2008, 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 import java.io.IOException;
  26 import java.io.Serializable;
  27 import java.net.Socket;
  28 import java.rmi.server.RMIClientSocketFactory;
  29 import java.util.HashMap;
  30 import javax.management.MBeanServer;
  31 import javax.management.MBeanServerFactory;
  32 import javax.management.Notification;
  33 import javax.management.NotificationBroadcasterSupport;
  34 import javax.management.NotificationListener;
  35 import javax.management.ObjectName;
  36 import javax.management.remote.JMXConnector;
  37 import javax.management.remote.JMXConnectorFactory;
  38 import javax.management.remote.JMXConnectorServer;
  39 import javax.management.remote.JMXConnectorServerFactory;
  40 import javax.management.remote.JMXServiceURL;
  41 import javax.management.remote.rmi.RMIConnectorServer;
  42 
  43 /*
  44  * @test
  45  * @bug 6697180
  46  * @summary test on a client notification deadlock.
  47  * @author Shanliang JIANG
  48  * @modules java.management
  49  * @run clean MultiThreadDeadLockTest
  50  * @run build MultiThreadDeadLockTest
  51  * @run main MultiThreadDeadLockTest
  52  */
  53 
  54 public class MultiThreadDeadLockTest {
  55 
  56     private static long serverTimeout = 500L;
  57 
  58     public static void main(String[] args) throws Exception {
  59         print("Create the MBean server");
  60         MBeanServer mbs = MBeanServerFactory.createMBeanServer();
  61 
  62         print("Initialize environment map");
  63         HashMap env = new HashMap();
  64 
  65         print("Specify a client socket factory to control socket creation.");
  66         env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
  67                 clientFactory);
  68 
  69         print("Specify a server idle timeout to make a server close an idle connection.");
  70         env.put("jmx.remote.x.server.connection.timeout", serverTimeout);
  71 
  72         print("Disable client heartbeat.");
  73         env.put("jmx.remote.x.client.connection.check.period", 0);
  74 
  75         env.put("jmx.remote.x.notification.fetch.timeout", serverTimeout);
  76 
  77         print("Create an RMI server");
  78         JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
  79         JMXConnectorServer server =
  80                 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
  81         server.start();
  82 
  83         url = server.getAddress();
  84 
  85         print("Create jmx client on "+url);
  86         StateMachine.setState(CREATE_SOCKET); // allow to create client socket
  87         client = JMXConnectorFactory.connect(url, env);
  88         Thread.sleep(100);
  89 
  90         totoName = new ObjectName("default:name=toto");
  91         mbs.registerMBean(toto, totoName);
  92         print("Register the mbean: " + totoName);
  93 
  94         print("Add listener to toto MBean");
  95         client.getMBeanServerConnection().addNotificationListener(
  96                 totoName, myListener, null, null);
  97         Thread.sleep(10);
  98 
  99         print("send notif, listener will block the fetcher");
 100         toto.sendNotif();
 101         Thread.sleep(100);
 102 
 103         StateMachine.setState(NO_OP);
 104 
 105         print("Sleep 3 times of server idle timeout: "+serverTimeout+
 106                 ", the sever should close the idle connection.");
 107         Thread.sleep(serverTimeout*3);
 108 
 109         print("start the user thread to call mbean method, it will get IOexception" +
 110                 " and start the reconnection, the socket factory will block the" +
 111                 " socket creation.");
 112         UserThread ut = new UserThread();
 113         ut.start();
 114         Thread.sleep(10);
 115 
 116         print("Free the listener, the fetcher will get IO and makes " +
 117                 "a deadlock if the bug is not fixed.");
 118         StateMachine.setState(FREE_LISTENER);
 119         Thread.sleep(100);
 120 
 121         print("Allow to create new socket for the reconnection");
 122         StateMachine.setState(CREATE_SOCKET);
 123 
 124         print("Check whether the user thread gets free to call the mbean.");
 125         if (!ut.waitDone(5000)) {
 126             throw new RuntimeException("Possible deadlock!");
 127         }
 128 
 129         print("Remove the listener.");
 130         client.getMBeanServerConnection().removeNotificationListener(
 131                 totoName, myListener, null, null);
 132         Thread.sleep(serverTimeout*3);
 133 
 134         print("\nWell passed, bye!");
 135 
 136         client.close();
 137         Thread.sleep(10);
 138         server.stop();
 139     }
 140 
 141     private static ObjectName totoName = null;
 142     private static JMXConnector client;
 143 
 144     public static class UserThread extends Thread {
 145         public UserThread() {
 146             setDaemon(true);
 147         }
 148 
 149         public void run() {
 150             try {
 151                 client.getMBeanServerConnection().invoke(
 152                         totoName, "allowReturn", null, null);
 153             } catch (Exception e) {
 154                 throw new Error(e);
 155             }
 156 
 157             synchronized(UserThread.class) {
 158                 done = true;
 159                 UserThread.class.notify();
 160             }
 161         }
 162 
 163         public boolean waitDone(long timeout) {
 164             synchronized(UserThread.class) {
 165                 if(!done) {
 166                     try {
 167                         UserThread.class.wait(timeout);
 168                     } catch (Exception e) {
 169                         throw new Error(e);
 170                     }
 171                 }
 172             }
 173             return done;
 174         }
 175 
 176         private boolean done = false;
 177     }
 178 
 179     public static interface TotoMBean {
 180         public void allowReturn();
 181     }
 182 
 183     public static class Toto extends NotificationBroadcasterSupport
 184             implements TotoMBean {
 185 
 186         public void allowReturn() {
 187             enter("allowReturn");
 188 
 189             leave("allowReturn");
 190         }
 191 
 192         public void sendNotif() {
 193             enter("sendNotif");
 194 
 195             sendNotification(new Notification("Toto", totoName, 0));
 196 
 197             leave("sendNotif");
 198         }
 199     }
 200     private static Toto toto = new Toto();
 201 
 202     public static NotificationListener myListener = new NotificationListener() {
 203         public void handleNotification(Notification notification, Object handback) {
 204             enter("handleNotification");
 205 
 206             StateMachine.waitState(FREE_LISTENER);
 207 
 208             leave("handleNotification");
 209         }
 210     };
 211 
 212     public static class RMIClientFactory
 213             implements RMIClientSocketFactory, Serializable {
 214 
 215         public Socket createSocket(String host, int port) throws IOException {
 216             enter("createSocket");
 217             //print("Calling createSocket(" + host + " " + port + ")");
 218 
 219             StateMachine.waitState(CREATE_SOCKET);
 220             Socket s = new Socket(host, port);
 221             leave("createSocket");
 222 
 223             return s;
 224         }
 225     }
 226     private static RMIClientFactory clientFactory = new RMIClientFactory();
 227 
 228     private static int CREATE_SOCKET = 1;
 229     private static int FREE_LISTENER = 3;
 230     private static int NO_OP = 0;
 231 
 232     public static class StateMachine {
 233 
 234         private static int state = NO_OP;
 235         private static int[] lock = new int[0];
 236 
 237         public static void waitState(int s) {
 238             synchronized (lock) {
 239                 while (state != s) {
 240                     try {
 241                         lock.wait();
 242                     } catch (InterruptedException ire) {
 243                         // should not
 244                         throw new Error(ire);
 245                     }
 246                 }
 247             }
 248         }
 249 
 250         public static int getState() {
 251             synchronized (lock) {
 252                 return state;
 253             }
 254         }
 255 
 256         public static void setState(int s) {
 257             synchronized (lock) {
 258                 state = s;
 259                 lock.notifyAll();
 260             }
 261         }
 262     }
 263 
 264     private static void print(String m) {
 265         System.out.println(m);
 266     }
 267 
 268     private static void enter(String m) {
 269         System.out.println("\n---Enter the method " + m);
 270     }
 271 
 272     private static void leave(String m) {
 273         System.out.println("===Leave the method: " + m);
 274     }
 275 }