1/*
2 * Copyright (c) 2003, 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 * @test
26 * @bug 4886838 4886830 8025204
27 * @summary Tests that idle timeouts happen at appropriate times
28 * @author Eamonn McManus
29 * @modules java.management.rmi
30 *          java.management/com.sun.jmx.remote.util
31 * @run clean IdleTimeoutTest
32 * @run build IdleTimeoutTest
33 * @run main IdleTimeoutTest
34 */
35
36import java.util.Arrays;
37import java.util.ArrayList;
38import java.util.Iterator;
39import java.util.List;
40import java.util.HashMap;
41import java.util.Map;
42import javax.management.MBeanServer;
43import javax.management.MBeanServerConnection;
44import javax.management.MBeanServerFactory;
45import javax.management.MBeanServerNotification;
46import javax.management.Notification;
47import javax.management.NotificationListener;
48import javax.management.ObjectName;
49import javax.management.remote.JMXConnector;
50import javax.management.remote.JMXConnectorFactory;
51import javax.management.remote.JMXConnectorServer;
52import javax.management.remote.JMXConnectorServerFactory;
53import javax.management.remote.JMXServiceURL;
54import com.sun.jmx.remote.util.EnvHelp;
55
56public class IdleTimeoutTest {
57
58    static boolean isPresent(String cn) {
59        try {
60            Class.forName(cn);
61            return true;
62        } catch (ClassNotFoundException x) {
63            return false;
64        }
65    }
66
67    public static void main(String[] args) throws Exception {
68        boolean ok = true;
69        List protos;
70        if (args.length > 0)
71            protos = Arrays.asList(args);
72        else {
73            protos = new ArrayList(Arrays.asList(new String[] {"rmi"}));
74            if (isPresent("javax.management.remote.rmi._RMIConnectionImpl_Tie"))
75                protos.add("iiop");
76            if (isPresent("javax.management.remote.jmxmp.JMXMPConnectorServer"))
77                protos.add("jmxmp");
78        }
79        for (Iterator it = protos.iterator(); it.hasNext(); ) {
80            String proto = (String) it.next();
81            int liCount;
82            if (proto.equals("jmxmp")) liCount=1;
83            else liCount=2;
84            if (test(proto,4,liCount))
85                System.out.println("Test for protocol " + proto + " passed");
86            else {
87                System.out.println("Test for protocol " + proto + " FAILED");
88                ok = false;
89            }
90        }
91        if (!ok) {
92            throw new RuntimeException("Some tests failed - see log for details");
93        }
94    }
95
96    private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
97        throws Exception
98    {
99        JMXConnectorServer server =
100            JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
101        server.start();
102        try {
103            url = server.getAddress();
104
105            // Force initialization (class loading, JIT, etc...)
106            //
107            JMXConnector client = JMXConnectorFactory.connect(url);
108            try {
109                String connId = client.getConnectionId();
110                MBeanServerConnection conn = client.getMBeanServerConnection();
111            } finally {
112                client.close();
113            }
114
115            // Do the time measurement
116            //
117            final long firstTime = System.currentTimeMillis();
118            final long endtime;
119            client = JMXConnectorFactory.connect(url);
120            try {
121                String connId = client.getConnectionId();
122                MBeanServerConnection conn = client.getMBeanServerConnection();
123                endtime = System.currentTimeMillis();
124            } finally {
125                client.close();
126            }
127
128            // multipled by 10 for a slow machine, plus 1500 for a fast one.
129            return 10*(endtime - firstTime) + 1500;
130        } finally {
131            server.stop();
132        }
133    }
134
135    private static class NotificationCounter
136        implements NotificationListener {
137        private final int[]  listenerCount;
138        private final String listenerName;
139        NotificationCounter(int[] counter, String name) {
140            listenerCount=counter;
141            listenerName=name;
142        }
143
144        public void handleNotification(Notification n,
145                                       Object h) {
146            MBeanServerNotification mbsn =
147                (MBeanServerNotification) n;
148            System.out.println(listenerName + " got notification: "
149                               + mbsn.getMBeanName());
150            synchronized (listenerCount) {
151                listenerCount[0]++;
152                listenerCount.notify();
153            }
154        }
155        public String toString() {
156            return listenerName;
157        }
158    }
159
160    private static boolean test(String proto,int opCount,int liCount)
161        throws Exception {
162        System.out.println("Idle timeout test for protocol " + proto);
163        ObjectName delegateName =
164            ObjectName.getInstance("JMImplementation:" +
165                                   "type=MBeanServerDelegate");
166        MBeanServer mbs = MBeanServerFactory.createMBeanServer();
167        JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + "://");
168
169        final long timeout = getIdleTimeout(mbs,url);
170        System.out.println("Timeout for " + proto + " is: " +
171                           timeout + " ms");
172
173        Map idleMap = new HashMap();
174        idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout));
175        JMXConnectorServer server =
176            JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs);
177
178        final int[] listenerCount = new int[1];
179        final NotificationListener countListeners[] =
180            new NotificationListener[liCount];
181        int i;
182        for (i=0; i<countListeners.length; i++) {
183            countListeners[i] =
184                new NotificationCounter(listenerCount,"Listener"+i);
185        }
186
187        server.start();
188        try {
189            url = server.getAddress();
190            final long firstTime = System.currentTimeMillis();
191            JMXConnector client = JMXConnectorFactory.connect(url);
192            long elapsed, startIdle=0;
193            try {
194                String connId = client.getConnectionId();
195                MBeanServerConnection conn =
196                    client.getMBeanServerConnection();
197                elapsed   = System.currentTimeMillis() - firstTime;
198                System.out.println("Idle Time: " + elapsed + "ms");
199
200                for (i=0; i<countListeners.length; i++) {
201                    System.out.println("add " + countListeners[i] +
202                                       ": starting at " + elapsed + "ms");
203                    conn.addNotificationListener(delegateName,
204                                                 countListeners[i],
205                                                 null,null);
206                }
207
208                System.out.println("connId=" + connId);
209                for (i = 0; i < opCount; i++) {
210                    elapsed   = System.currentTimeMillis() - firstTime;
211                    System.out.println("Operation[" + (i+1)
212                                       +"]: starting at " +
213                                       elapsed + "ms");
214                    final String name = "d:type=mlet,instance=" + i;
215                    mbs.createMBean("javax.management.loading.MLet",
216                                    new ObjectName(name));
217                    if (i == (opCount-1))
218                        startIdle = System.currentTimeMillis();
219                    Thread.sleep(2);
220                }
221
222                // Wait for notifs to arrive before doing removeNListener
223                long startTime = System.currentTimeMillis();
224                long deadline = startTime + 10000;
225
226                System.out.println("Waiting for notifs: starting at " +
227                                   (startTime - firstTime) + "ms");
228
229                final int expectedCount = opCount*countListeners.length;
230                while (System.currentTimeMillis() < deadline) {
231                    synchronized (listenerCount) {
232                        if (listenerCount[0] >= expectedCount)
233                            break;
234                        listenerCount.wait();
235                    }
236                }
237
238                long elapsedWait = System.currentTimeMillis() - startTime;
239                System.out.println("Waited " + elapsedWait +
240                                   "ms for notifs to arrive");
241
242                if (listenerCount[0] != expectedCount) {
243                    System.out.println("Did not get expected " +
244                                       expectedCount + " notifications: "
245                                       + listenerCount[0]);
246                    return false;
247                }
248
249                elapsed   = System.currentTimeMillis() - firstTime;
250
251                System.out.println("idle time since last operation: " +
252                                   (elapsed + firstTime - startIdle) + "ms");
253                System.out.println("Requesting conn id at: " +
254                                   elapsed + "ms");
255                final String cid = client.getConnectionId();
256
257                elapsed   = System.currentTimeMillis() - firstTime;
258                System.out.println("Got conn id <" + cid + "> at: " +
259                                   elapsed + "ms");
260
261                if (!connId.equals(cid)) {
262                    System.out.println("Client id changed: <" + connId +
263                                       "> -> <" + cid +
264                                       ">");
265                    return false;
266                }
267
268                List ids = Arrays.asList(server.getConnectionIds());
269                if (!ids.contains(connId)) {
270                    System.out.println("Server ids don't contain our id: " +
271                                       ids + " - " + connId);
272                    return false;
273                }
274
275                for (i=0;i<countListeners.length;i++) {
276                    System.out.println("Removing notification listener: " +
277                                       countListeners[i]);
278                    conn.removeNotificationListener(delegateName,
279                                                    countListeners[i]);
280                }
281
282                System.out.println("Waiting for id list to drop ours");
283                // pass or timed out by test harness - see 8025204
284                do {
285                   Thread.sleep(100);
286                   ids = Arrays.asList(server.getConnectionIds());
287                } while (ids.contains(connId));
288
289                conn.getDefaultDomain();
290                if (connId.equals(client.getConnectionId())) {
291                    System.out.println("Client id did not change: <" + connId +
292                                       ">: idle timeout did not happen?");
293                    return false;
294                } else {
295                    System.out.println("Client id changed as expected: <" +
296                                       connId + "> -> <" +
297                                       client.getConnectionId() + ">");
298                }
299            } finally {
300                client.close();
301                System.out.println("Connection id list on server after " +
302                                   "client close: " +
303                                   Arrays.asList(server.getConnectionIds()));
304            }
305        } finally {
306            server.stop();
307        }
308        System.out.println("*** ------------------------------------------");
309        System.out.println("*** Test passed for " + proto);
310        System.out.println("*** ------------------------------------------");
311        return true;
312    }
313}
314