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
25import java.io.IOException;
26import java.io.Serializable;
27import java.net.Socket;
28import java.rmi.server.RMIClientSocketFactory;
29import java.util.HashMap;
30import javax.management.MBeanServer;
31import javax.management.MBeanServerFactory;
32import javax.management.Notification;
33import javax.management.NotificationBroadcasterSupport;
34import javax.management.NotificationListener;
35import javax.management.ObjectName;
36import javax.management.remote.JMXConnector;
37import javax.management.remote.JMXConnectorFactory;
38import javax.management.remote.JMXConnectorServer;
39import javax.management.remote.JMXConnectorServerFactory;
40import javax.management.remote.JMXServiceURL;
41import 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 *
49 * @run clean MultiThreadDeadLockTest
50 * @run build MultiThreadDeadLockTest
51 * @run main MultiThreadDeadLockTest
52 */
53
54public 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}
276