| /* |
| * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.net.Socket; |
| import java.rmi.server.RMIClientSocketFactory; |
| import java.util.HashMap; |
| import javax.management.MBeanServer; |
| import javax.management.MBeanServerFactory; |
| import javax.management.Notification; |
| import javax.management.NotificationBroadcasterSupport; |
| import javax.management.NotificationListener; |
| import javax.management.ObjectName; |
| import javax.management.remote.JMXConnector; |
| import javax.management.remote.JMXConnectorFactory; |
| import javax.management.remote.JMXConnectorServer; |
| import javax.management.remote.JMXConnectorServerFactory; |
| import javax.management.remote.JMXServiceURL; |
| import javax.management.remote.rmi.RMIConnectorServer; |
| |
| /* |
| * @test |
| * @bug 6697180 |
| * @summary test on a client notification deadlock. |
| * @author Shanliang JIANG |
| * |
| * @run clean MultiThreadDeadLockTest |
| * @run build MultiThreadDeadLockTest |
| * @run main MultiThreadDeadLockTest |
| */ |
| |
| public class MultiThreadDeadLockTest { |
| |
| private static long serverTimeout = 500L; |
| |
| public static void main(String[] args) throws Exception { |
| print("Create the MBean server"); |
| MBeanServer mbs = MBeanServerFactory.createMBeanServer(); |
| |
| print("Initialize environment map"); |
| HashMap env = new HashMap(); |
| |
| print("Specify a client socket factory to control socket creation."); |
| env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, |
| clientFactory); |
| |
| print("Specify a server idle timeout to make a server close an idle connection."); |
| env.put("jmx.remote.x.server.connection.timeout", serverTimeout); |
| |
| print("Disable client heartbeat."); |
| env.put("jmx.remote.x.client.connection.check.period", 0); |
| |
| env.put("jmx.remote.x.notification.fetch.timeout", serverTimeout); |
| |
| print("Create an RMI server"); |
| JMXServiceURL url = new JMXServiceURL("rmi", null, 0); |
| JMXConnectorServer server = |
| JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); |
| server.start(); |
| |
| url = server.getAddress(); |
| |
| print("Create jmx client on "+url); |
| StateMachine.setState(CREATE_SOCKET); // allow to create client socket |
| client = JMXConnectorFactory.connect(url, env); |
| Thread.sleep(100); |
| |
| totoName = new ObjectName("default:name=toto"); |
| mbs.registerMBean(toto, totoName); |
| print("Register the mbean: " + totoName); |
| |
| print("Add listener to toto MBean"); |
| client.getMBeanServerConnection().addNotificationListener( |
| totoName, myListener, null, null); |
| Thread.sleep(10); |
| |
| print("send notif, listener will block the fetcher"); |
| toto.sendNotif(); |
| Thread.sleep(100); |
| |
| StateMachine.setState(NO_OP); |
| |
| print("Sleep 3 times of server idle timeout: "+serverTimeout+ |
| ", the sever should close the idle connection."); |
| Thread.sleep(serverTimeout*3); |
| |
| print("start the user thread to call mbean method, it will get IOexception" + |
| " and start the reconnection, the socket factory will block the" + |
| " socket creation."); |
| UserThread ut = new UserThread(); |
| ut.start(); |
| Thread.sleep(10); |
| |
| print("Free the listener, the fetcher will get IO and makes " + |
| "a deadlock if the bug is not fixed."); |
| StateMachine.setState(FREE_LISTENER); |
| Thread.sleep(100); |
| |
| print("Allow to create new socket for the reconnection"); |
| StateMachine.setState(CREATE_SOCKET); |
| |
| print("Check whether the user thread gets free to call the mbean."); |
| if (!ut.waitDone(5000)) { |
| throw new RuntimeException("Possible deadlock!"); |
| } |
| |
| print("Remove the listener."); |
| client.getMBeanServerConnection().removeNotificationListener( |
| totoName, myListener, null, null); |
| Thread.sleep(serverTimeout*3); |
| |
| print("\nWell passed, bye!"); |
| |
| client.close(); |
| Thread.sleep(10); |
| server.stop(); |
| } |
| |
| private static ObjectName totoName = null; |
| private static JMXConnector client; |
| |
| public static class UserThread extends Thread { |
| public UserThread() { |
| setDaemon(true); |
| } |
| |
| public void run() { |
| try { |
| client.getMBeanServerConnection().invoke( |
| totoName, "allowReturn", null, null); |
| } catch (Exception e) { |
| throw new Error(e); |
| } |
| |
| synchronized(UserThread.class) { |
| done = true; |
| UserThread.class.notify(); |
| } |
| } |
| |
| public boolean waitDone(long timeout) { |
| synchronized(UserThread.class) { |
| if(!done) { |
| try { |
| UserThread.class.wait(timeout); |
| } catch (Exception e) { |
| throw new Error(e); |
| } |
| } |
| } |
| return done; |
| } |
| |
| private boolean done = false; |
| } |
| |
| public static interface TotoMBean { |
| public void allowReturn(); |
| } |
| |
| public static class Toto extends NotificationBroadcasterSupport |
| implements TotoMBean { |
| |
| public void allowReturn() { |
| enter("allowReturn"); |
| |
| leave("allowReturn"); |
| } |
| |
| public void sendNotif() { |
| enter("sendNotif"); |
| |
| sendNotification(new Notification("Toto", totoName, 0)); |
| |
| leave("sendNotif"); |
| } |
| } |
| private static Toto toto = new Toto(); |
| |
| public static NotificationListener myListener = new NotificationListener() { |
| public void handleNotification(Notification notification, Object handback) { |
| enter("handleNotification"); |
| |
| StateMachine.waitState(FREE_LISTENER); |
| |
| leave("handleNotification"); |
| } |
| }; |
| |
| public static class RMIClientFactory |
| implements RMIClientSocketFactory, Serializable { |
| |
| public Socket createSocket(String host, int port) throws IOException { |
| enter("createSocket"); |
| //print("Calling createSocket(" + host + " " + port + ")"); |
| |
| StateMachine.waitState(CREATE_SOCKET); |
| Socket s = new Socket(host, port); |
| leave("createSocket"); |
| |
| return s; |
| } |
| } |
| private static RMIClientFactory clientFactory = new RMIClientFactory(); |
| |
| private static int CREATE_SOCKET = 1; |
| private static int FREE_LISTENER = 3; |
| private static int NO_OP = 0; |
| |
| public static class StateMachine { |
| |
| private static int state = NO_OP; |
| private static int[] lock = new int[0]; |
| |
| public static void waitState(int s) { |
| synchronized (lock) { |
| while (state != s) { |
| try { |
| lock.wait(); |
| } catch (InterruptedException ire) { |
| // should not |
| throw new Error(ire); |
| } |
| } |
| } |
| } |
| |
| public static int getState() { |
| synchronized (lock) { |
| return state; |
| } |
| } |
| |
| public static void setState(int s) { |
| synchronized (lock) { |
| state = s; |
| lock.notifyAll(); |
| } |
| } |
| } |
| |
| private static void print(String m) { |
| System.out.println(m); |
| } |
| |
| private static void enter(String m) { |
| System.out.println("\n---Enter the method " + m); |
| } |
| |
| private static void leave(String m) { |
| System.out.println("===Leave the method: " + m); |
| } |
| } |