Path: blob/master/test/jdk/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java
41155 views
/*1* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 623940026* @summary Tests NotificationBuffer doesn't hold locks when adding listeners,27* if test times out then deadlock is suspected.28* @author Eamonn McManus29*30* @run clean NotificationBufferDeadlockTest31* @run build NotificationBufferDeadlockTest32* @run main NotificationBufferDeadlockTest33*/3435import java.lang.reflect.InvocationHandler;36import java.lang.reflect.Method;37import java.lang.reflect.Proxy;38import java.net.MalformedURLException;39import java.util.List;40import java.util.Set;41import java.util.Vector;42import java.util.concurrent.CountDownLatch;43import javax.management.*;44import javax.management.remote.*;4546/*47* Regression test for a rare but not unheard-of deadlock condition in48* the notification buffer support for connector servers.49* See bug 6239400 for the description of the bug and the example that50* showed it up.51*52* Here we test that, when the connector server adds its listener to an53* MBean, it is not holding a lock that would prevent another thread from54* emitting a notification (from that MBean or another one). This is55* important, because we don't know how user MBeans might implement56* NotificationBroadcaster.addNotificationListener, and in particular we57* can't be sure that the method is well-behaved and can never do a58* blocking operation, such as attempting to acquire a lock that is also59* acquired when notifications are emitted.60*61* The test creates a special MBean whose addNotificationListener method62* does the standard addNotificationListener logic inherited63* from NotificationBroadcasterSupport, then64* creates another thread that emits a notification from the same MBean.65* The addNotificationListener method waits for this thread to complete.66* If the notification buffer logic is incorrect, then emitting the67* notification will attempt to acquire the lock on the buffer, but that68* lock is being held by the thread that called addNotificationListener,69* so there will be deadlock.70*71* We use this DeadlockMBean several times. First, we create one and then72* add a remote listener to it. The first time you add a remote listener73* through a connector server, the connector server adds its own listener74* to all NotificationBroadcaster MBeans. If it holds a lock while doing75* this, we will see deadlock.76*77* Then we create a second DeadlockMBean. When a new MBean is created that78* is a NotificationBroadcaster, the connector server adds its listener to79* that MBean too. Again if it holds a lock while doing this, we will see80* deadlock.81*82* Finally, we do some magic with MBeanServerForwarders so that while83* queryNames is running (to find MBeans to which listeners must be added)84* we will create new MBeans. This tests that this tricky situation is85* handled correctly. It also tests the queryNames that is run when the86* notification buffer is being destroyed (to remove the listeners).87*88* We cause all of our test MBeans to emit exactly one notification and89* check that we have received exactly one notification from each MBean.90* If the logic for adding the notification buffer's listener is incorrect91* we could remove zero or two notifications from an MBean.92*/93public class NotificationBufferDeadlockTest {94public static void main(String[] args) throws Exception {95System.out.println("Check no deadlock if notif sent while initial " +96"remote listeners being added");97final String[] protos = {"rmi", "iiop", "jmxmp"};98for (String p : protos) {99try {100test(p);101} catch (Exception e) {102System.out.println("TEST FAILED: GOT EXCEPTION:");103e.printStackTrace(System.out);104failure = e.toString();105}106}107if (failure == null)108return;109else110throw new Exception("TEST FAILED: " + failure);111}112113private static void test(String proto) throws Exception {114System.out.println("Testing protocol " + proto);115MBeanServer mbs = MBeanServerFactory.newMBeanServer();116ObjectName testName = newName();117DeadlockTest test = new DeadlockTest();118mbs.registerMBean(test, testName);119JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + ":///");120JMXConnectorServer cs;121try {122cs =123JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);124} catch (MalformedURLException e) {125System.out.println("...protocol not supported, ignoring");126return;127}128129MBeanServerForwarder createDuringQueryForwarder = (MBeanServerForwarder)130Proxy.newProxyInstance(new Object() {}.getClass().getClassLoader(),131new Class[] {MBeanServerForwarder.class},132new CreateDuringQueryInvocationHandler());133cs.setMBeanServerForwarder(createDuringQueryForwarder);134cs.start();135JMXServiceURL addr = cs.getAddress();136JMXConnector cc = JMXConnectorFactory.connect(addr);137MBeanServerConnection mbsc = cc.getMBeanServerConnection();138try {139String fail = test(mbsc, testName);140if (fail != null)141System.out.println("FAILED: " + fail);142failure = fail;143} finally {144cc.close();145cs.stop();146}147}148149private static String test(MBeanServerConnection mbsc,150ObjectName testName) throws Exception {151152NotificationListener dummyListener = new NotificationListener() {153public void handleNotification(Notification n, Object h) {154}155};156thisFailure = null;157mbsc.addNotificationListener(testName, dummyListener, null, null);158if (thisFailure != null)159return thisFailure;160ObjectName newName = newName();161mbsc.createMBean(DeadlockTest.class.getName(), newName);162if (thisFailure != null)163return thisFailure;164Set<ObjectName> names =165mbsc.queryNames(new ObjectName("d:type=DeadlockTest,*"), null);166System.out.printf("...found %d test MBeans\n", names.size());167168sources.clear();169countListener = new MyListener(names.size());170171for (ObjectName name : names)172mbsc.addNotificationListener(name, countListener, null, null);173if (thisFailure != null)174return thisFailure;175for (ObjectName name : names)176mbsc.invoke(name, "send", null, null);177178countListener.waiting();179180if (!sources.containsAll(names))181return "missing names: " + sources;182return thisFailure;183}184185public static interface DeadlockTestMBean {186public void send();187}188189public static class DeadlockTest extends NotificationBroadcasterSupport190implements DeadlockTestMBean {191@Override192public void addNotificationListener(NotificationListener listener,193NotificationFilter filter,194Object handback) {195super.addNotificationListener(listener, filter, handback);196Thread t = new Thread() {197@Override198public void run() {199Notification n =200new Notification("type", DeadlockTest.this, 0L);201DeadlockTest.this.sendNotification(n);202}203};204t.start();205System.out.println("DeadlockTest-addNotificationListener waiting for the sending thread to die...");206try {207t.join(); //if times out here then deadlock is suspected208System.out.println("DeadlockTest-addNotificationListener OK.");209} catch (Exception e) {210thisFailure = "Join exception: " + e;211}212}213214public void send() {215sendNotification(new Notification(TESTING_TYPE, DeadlockTest.this, 1L));216}217}218219private static class CreateDuringQueryInvocationHandler220implements InvocationHandler {221public Object invoke(Object proxy, Method m, Object[] args)222throws Throwable {223if (m.getName().equals("setMBeanServer")) {224mbs = (MBeanServer) args[0];225return null;226}227createMBeanIfQuery(m);228Object ret = m.invoke(mbs, args);229createMBeanIfQuery(m);230return ret;231}232233private void createMBeanIfQuery(Method m) throws InterruptedException {234if (m.getName().equals("queryNames")) {235Thread t = new Thread() {236public void run() {237try {238mbs.createMBean(DeadlockTest.class.getName(),239newName());240} catch (Exception e) {241e.printStackTrace();242thisFailure = e.toString();243}244}245};246t.start();247System.out.println("CreateDuringQueryInvocationHandler-createMBeanIfQuery waiting for the creating thread to die...");248t.join(); // if times out here then deadlock is suspected249System.out.println("CreateDuringQueryInvocationHandler-createMBeanIfQuery OK");250}251}252253private MBeanServer mbs;254}255256private static synchronized ObjectName newName() {257try {258return new ObjectName("d:type=DeadlockTest,instance=" +259++nextNameIndex);260} catch (MalformedObjectNameException e) {261throw new IllegalArgumentException("bad ObjectName", e);262}263}264265private static class MyListener implements NotificationListener {266public MyListener(int waitNB) {267count = new CountDownLatch(waitNB);268}269270public void handleNotification(Notification n, Object h) {271System.out.println("MyListener got: " + n.getSource() + " " + n.getType());272273if (TESTING_TYPE.equals(n.getType())) {274sources.add((ObjectName) n.getSource());275count.countDown();276}277}278279public void waiting() throws InterruptedException {280System.out.println("MyListener-waiting ...");281count.await(); // if times out here then deadlock is suspected282System.out.println("MyListener-waiting done!");283}284285private final CountDownLatch count;286}287288static String thisFailure;289static String failure;290static int nextNameIndex;291292private static MyListener countListener;293private static final List<ObjectName> sources = new Vector();294295private static final String TESTING_TYPE = "testing_type";296}297298299