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 }