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