1 /*
   2  * Copyright (c) 2003, 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 4757273
  27  * @summary Test deadlock in MBeanServerDelegate listeners
  28  * @author Eamonn McManus
  29  * @run clean NotifDeadlockTest
  30  * @run build NotifDeadlockTest
  31  * @run main NotifDeadlockTest
  32  */
  33 
  34 /*
  35  * Test deadlock when a listener for an MBeanServerDelegate does a
  36  * register or unregister of an MBean.  Since such a listener is
  37  * triggered by a register or unregister operation, deadlock scenarios
  38  * are possible if there are any locks held while the listener is
  39  * being dispatched.
  40  *
  41  * The flow of control looks rather like this:
  42  *
  43  * Thread 1:
  44  * - MBeanServer.createMBean(..., objectName1);
  45  * --- MBeanServerDelegate.sendNotification
  46  * ----- XListener.handleNotification
  47  * ------- create Thread 2
  48  * ------- wait for Thread 2 to complete
  49  *
  50  * Thread 2:
  51  * - MBeanServer.createMBean(..., objectName2);
  52  * - end Thread 2
  53  *
  54  * If any locks are held by Thread 1 within createMBean or
  55  * sendNotification, then Thread 2 can block waiting for them.
  56  * Since Thread 1 is itself waiting for Thread 2, this is a deadlock.
  57  *
  58  * We test all four combinations of:
  59  * (Thread1-create,Thread1-unregister) x (Thread2-create,Thread2-unregister)
  60  *
  61  * In the JMX 1.1 RI, all four tests fail.  In the JMX 1.2 RI, all four
  62  * tests should pass.
  63  */
  64 import javax.management.*;
  65 
  66 public class NotifDeadlockTest {
  67     static ObjectName on1, on2, delName;
  68     static {
  69         try {
  70             on1 = new ObjectName("thing:a=b");
  71             on2 = new ObjectName("thing:c=d");
  72             delName =
  73                 new ObjectName("JMImplementation:type=MBeanServerDelegate");
  74         } catch (MalformedObjectNameException e) {
  75             throw new Error();
  76         }
  77     }
  78     static MBeanServer mbs;
  79     static boolean timedOut;
  80 
  81     /* This listener registers or unregisters the MBean called on2
  82        when triggered.  */
  83     private static class XListener implements NotificationListener {
  84         private boolean firstTime = true;
  85         private final boolean register;
  86 
  87         XListener(boolean register) {
  88             this.register = register;
  89         }
  90 
  91         public void handleNotification(Notification not, Object handback) {
  92             if (firstTime) {
  93                 firstTime = false;
  94                 Thread t = new Thread() {
  95                     public void run() {
  96                         try {
  97                             if (register) {
  98                                 mbs.createMBean("javax.management.timer.Timer",
  99                                                 on2);
 100                                 System.out.println("Listener created " + on2);
 101                             } else {
 102                                 mbs.unregisterMBean(on2);
 103                                 System.out.println("Listener removed " + on2);
 104                             }
 105                         } catch (Exception e) {
 106                             e.printStackTrace();
 107                         }
 108                     }
 109                 };
 110                 t.start();
 111                 try {
 112                     t.join(2000);
 113                 } catch (InterruptedException e) {
 114                     e.printStackTrace(); // should not happen
 115                 }
 116                 if (t.isAlive()) {
 117                     System.out.println("FAILURE: Wait timed out: " +
 118                                        "probable deadlock");
 119                     timedOut = true;
 120                 }
 121             }
 122         }
 123     }
 124 
 125     public static void main(String[] args) throws Exception {
 126         boolean success = true;
 127 
 128         System.out.println("Test 1: in register notif, unregister an MBean");
 129         timedOut = false;
 130         mbs = MBeanServerFactory.createMBeanServer();
 131         mbs.createMBean("javax.management.timer.Timer", on2);
 132         mbs.addNotificationListener(delName, new XListener(false), null, null);
 133         mbs.createMBean("javax.management.timer.Timer", on1);
 134         MBeanServerFactory.releaseMBeanServer(mbs);
 135         if (timedOut) {
 136             success = false;
 137             Thread.sleep(500);
 138             // wait for the spawned thread to complete its work, probably
 139         }
 140         System.out.println("Test 1 completed");
 141 
 142         System.out.println("Test 2: in unregister notif, unregister an MBean");
 143         timedOut = false;
 144         mbs = MBeanServerFactory.createMBeanServer();
 145         mbs.createMBean("javax.management.timer.Timer", on1);
 146         mbs.createMBean("javax.management.timer.Timer", on2);
 147         mbs.addNotificationListener(delName, new XListener(false), null, null);
 148         mbs.unregisterMBean(on1);
 149         MBeanServerFactory.releaseMBeanServer(mbs);
 150         if (timedOut) {
 151             success = false;
 152             Thread.sleep(500);
 153             // wait for the spawned thread to complete its work, probably
 154         }
 155         System.out.println("Test 2 completed");
 156 
 157         System.out.println("Test 3: in register notif, register an MBean");
 158         timedOut = false;
 159         mbs = MBeanServerFactory.createMBeanServer();
 160         mbs.addNotificationListener(delName, new XListener(true), null, null);
 161         mbs.createMBean("javax.management.timer.Timer", on1);
 162         MBeanServerFactory.releaseMBeanServer(mbs);
 163         if (timedOut) {
 164             success = false;
 165             Thread.sleep(500);
 166             // wait for the spawned thread to complete its work, probably
 167         }
 168         System.out.println("Test 3 completed");
 169 
 170         System.out.println("Test 4: in unregister notif, register an MBean");
 171         timedOut = false;
 172         mbs = MBeanServerFactory.createMBeanServer();
 173         mbs.createMBean("javax.management.timer.Timer", on1);
 174         mbs.addNotificationListener(delName, new XListener(true), null, null);
 175         mbs.unregisterMBean(on1);
 176         MBeanServerFactory.releaseMBeanServer(mbs);
 177         if (timedOut) {
 178             success = false;
 179             Thread.sleep(500);
 180             // wait for the spawned thread to complete its work, probably
 181         }
 182         System.out.println("Test 4 completed");
 183 
 184         if (success)
 185             System.out.println("Test passed");
 186         else {
 187             System.out.println("TEST FAILED: at least one subcase failed");
 188             System.exit(1);
 189         }
 190     }
 191 }