jdk/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java
author emcmanus
Thu, 27 Nov 2008 15:44:32 +0100
changeset 1636 eb801ce73ac9
parent 1247 b4c26443dee5
permissions -rw-r--r--
6776225: JMX.isNotificationSource wrong when DynamicWrapperMBean + SendNotification injection Reviewed-by: dfuchs, jfdenise

/*
 * Copyright 2007-2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/*
 * @test DynamicWrapperMBeanTest
 * @bug 6624232 6776225
 * @summary Test the DynamicWrapperMBean interface
 * @author Eamonn McManus
 */

import java.lang.management.ManagementFactory;
import javax.annotation.Resource;
import javax.management.JMX;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationInfo;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.SendNotification;
import javax.management.StandardEmitterMBean;
import javax.management.StandardMBean;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import javax.management.modelmbean.RequiredModelMBean;
import static javax.management.StandardMBean.Options;

public class DynamicWrapperMBeanTest {
    private static String failure;

    public static void main(String[] args) throws Exception {
        wrapTest();
        notifTest();

        if (failure == null)
            System.out.println("TEST PASSED");
        else
            throw new Exception("TEST FAILED: " + failure);
    }

    private static final Options wrappedVisOpts = new Options();
    private static final Options wrappedInvisOpts = new Options();
    static {
        wrappedVisOpts.setWrappedObjectVisible(true);
        wrappedInvisOpts.setWrappedObjectVisible(false);
    }

    public static interface WrappedMBean {
        public void sayHello();
    }
    public static class Wrapped implements WrappedMBean {
        public void sayHello() {
            System.out.println("Hello");
        }
    }

    private static void wrapTest() throws Exception {
        if (Wrapped.class.getClassLoader() ==
                StandardMBean.class.getClassLoader()) {
            throw new Exception(
                    "TEST ERROR: Resource and StandardMBean have same ClassLoader");
        }

        assertEquals("Options withWrappedObjectVisible(false)",
                     new Options(), wrappedInvisOpts);

        Wrapped resource = new Wrapped();

        StandardMBean visible =
                new StandardMBean(resource, WrappedMBean.class, wrappedVisOpts);
        StandardMBean invisible =
                new StandardMBean(resource, WrappedMBean.class, wrappedInvisOpts);

        assertEquals("getResource withWrappedObjectVisible(true)",
                resource, visible.getWrappedObject());
        assertEquals("getResource withWrappedObjectVisible(false)",
                invisible, invisible.getWrappedObject());

        System.out.println("===Testing StandardMBean===");

        ObjectName visibleName = new ObjectName("a:type=visible");
        ObjectName invisibleName = new ObjectName("a:type=invisible");
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        mbs.registerMBean(visible, visibleName);
        mbs.registerMBean(invisible, invisibleName);

        assertEquals("ClassLoader for visible resource",
                Wrapped.class.getClassLoader(),
                mbs.getClassLoaderFor(visibleName));
        assertEquals("ClassLoader for invisible resource",
                StandardMBean.class.getClassLoader(),
                mbs.getClassLoaderFor(invisibleName));

        assertEquals("isInstanceOf(WrappedMBean) for visible wrapped",
                true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
        assertEquals("isInstanceOf(WrappedMBean) for invisible wrapped",
                false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
        assertEquals("isInstanceOf(StandardMBean) for visible wrapped",
                false, mbs.isInstanceOf(visibleName, StandardMBean.class.getName()));
        assertEquals("isInstanceOf(StandardMBean) for invisible wrapped",
                true, mbs.isInstanceOf(invisibleName, StandardMBean.class.getName()));

        mbs.unregisterMBean(visibleName);
        mbs.unregisterMBean(invisibleName);

        System.out.println("===Testing RequiredModelMBean===");

        // Godawful Model MBeans...
        ModelMBeanOperationInfo mmboi = new ModelMBeanOperationInfo(
                "say hello to the nice man", Wrapped.class.getMethod("sayHello"));
        ModelMBeanInfo visibleMmbi = new ModelMBeanInfoSupport(
                Wrapped.class.getName(), "Visible wrapped", null, null,
                new ModelMBeanOperationInfo[] {mmboi}, null);
        ModelMBeanInfo invisibleMmbi = new ModelMBeanInfoSupport(
                Wrapped.class.getName(), "Invisible wrapped", null, null,
                new ModelMBeanOperationInfo[] {mmboi}, null);
        RequiredModelMBean visibleRmmb = new RequiredModelMBean(visibleMmbi);
        RequiredModelMBean invisibleRmmb = new RequiredModelMBean(invisibleMmbi);
        visibleRmmb.setManagedResource(resource, "VisibleObjectReference");
        invisibleRmmb.setManagedResource(resource, "ObjectReference");

        mbs.registerMBean(visibleRmmb, visibleName);
        mbs.registerMBean(invisibleRmmb, invisibleName);

        assertEquals("ClassLoader for visible wrapped",
                Wrapped.class.getClassLoader(),
                mbs.getClassLoaderFor(visibleName));
        assertEquals("ClassLoader for invisible wrapped",
                StandardMBean.class.getClassLoader(),
                mbs.getClassLoaderFor(invisibleName));

        assertEquals("isInstanceOf(WrappedMBean) for visible resource",
                true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
        assertEquals("isInstanceOf(WrappedMBean) for invisible resource",
                false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
        assertEquals("isInstanceOf(RequiredModelMBean) for visible resource",
                false, mbs.isInstanceOf(visibleName, RequiredModelMBean.class.getName()));
        assertEquals("isInstanceOf(RequiredModelMBean) for invisible resource",
                true, mbs.isInstanceOf(invisibleName, RequiredModelMBean.class.getName()));

        mbs.unregisterMBean(visibleName);
        mbs.unregisterMBean(invisibleName);
    }

    private static enum WrapType {
        NBS("NotificationBroadcasterSupport"),
        INJ("@Resource SendNotification"),
        STD_MBEAN_NBS("StandardMBean delegating to NotificationBroadcasterSupport"),
        STD_MBEAN_INJ("StandardMBean delegating to @Resource SendNotification"),
        STD_MBEAN_SUB_NBS("StandardMBean subclass implementing NotificationBroadcaster"),
        STD_MBEAN_SUB_INJ("StandardMBean subclass with @Resource SendNotification"),
        STD_EMIT_MBEAN_NBS("StandardEmitterMBean delegating to NotificationBroadcasterSupport"),
        STD_EMIT_MBEAN_INJ("StandardEmitterMBean delegating to @Resource SendNotification"),
        STD_EMIT_MBEAN_SUB("StandardEmitterMBean subclass"),
        STD_EMIT_MBEAN_SUB_INJ("StandardEmitterMBean subclass with @Resource SendNotification");

        WrapType(String s) {
            this.s = s;
        }

        @Override
        public String toString() {
            return s;
        }

        private final String s;
    }

    @NotificationInfo(
        types = {"foo", "bar"}
    )
    public static interface BroadcasterMBean {
        public void send(Notification n);
    }

    public static class Broadcaster
            extends NotificationBroadcasterSupport implements BroadcasterMBean {
        public void send(Notification n) {
            super.sendNotification(n);
        }
    }

    public static interface SendNotifMBean extends BroadcasterMBean {
    }

    public static class SendNotif implements SendNotifMBean {
        @Resource
        private volatile SendNotification sendNotif;

        public void send(Notification n) {
            sendNotif.sendNotification(n);
        }
    }

    public static class StdBroadcaster
            extends StandardMBean
            implements BroadcasterMBean, NotificationBroadcaster {
        private final NotificationBroadcasterSupport nbs =
                new NotificationBroadcasterSupport();

        public StdBroadcaster() throws Exception {
            super(BroadcasterMBean.class);
        }

        public void send(Notification n) {
            nbs.sendNotification(n);
        }

        public void addNotificationListener(NotificationListener listener,
                NotificationFilter filter, Object handback) {
            nbs.addNotificationListener(listener, filter, handback);
        }

        public MBeanNotificationInfo[] getNotificationInfo() {
            return null;
        }

        public void removeNotificationListener(NotificationListener listener)
                throws ListenerNotFoundException {
            nbs.removeNotificationListener(listener);
        }
    }

    public static class StdSendNotif
            extends StandardMBean implements SendNotifMBean {
        @Resource
        private volatile SendNotification sendNotif;

        public StdSendNotif() throws Exception {
            super(SendNotifMBean.class);
        }

        public void send(Notification n) {
            sendNotif.sendNotification(n);
        }
    }

    public static class StdEmitterBroadcaster // :-)
            extends StandardEmitterMBean
            implements BroadcasterMBean {

        public StdEmitterBroadcaster() throws Exception {
            super(BroadcasterMBean.class, null);
        }

        public void send(Notification n) {
            super.sendNotification(n);
        }
    }

    // This case is unlikely - if you're using @Resource SendNotification
    // then there's no point in using StandardEmitterMBean, since
    // StandardMBean would suffice.
    public static class StdEmitterSendNotif
            extends StandardEmitterMBean implements SendNotifMBean {
        @Resource
        private volatile SendNotification sendNotif;

        public StdEmitterSendNotif() {
            super(SendNotifMBean.class, null);
        }

        public void send(Notification n) {
            sendNotif.sendNotification(n);
        }
    }

    // Test that JMX.isNotificationSource and
    // mbs.isInstanceOf("NotificationBroadcaster") work correctly even when
    // the MBean is a broadcaster by virtue of its wrapped resource.
    // Test that we find the MBeanNotificationInfo[] from the @NotificationInfo
    // annotation on BroadcasterMBean.  We cover a large number of different
    // MBean types, but all ultimately implement that interface.
    private static void notifTest() throws Exception {
        System.out.println("===Testing notification senders===");

        for (WrapType wrapType : WrapType.values()) {
            System.out.println("---" + wrapType);

            final Object mbean;

            switch (wrapType) {
            case NBS:
                // An MBean that extends NotificationBroadcasterSupport
                mbean = new Broadcaster();
                break;
            case INJ:
                // An MBean that injects SendNotification
                mbean = new SendNotif();
                break;
            case STD_MBEAN_NBS:
                // A StandardMBean that delegates to a NotificationBroadcasterSupport
                mbean = new StandardMBean(
                        new Broadcaster(), BroadcasterMBean.class, wrappedVisOpts);
                break;
            case STD_MBEAN_INJ:
                // A StandardMBean that delegates to an object that injects
                // SendNotification
                mbean = new StandardMBean(
                        new SendNotif(), BroadcasterMBean.class, wrappedVisOpts);
                break;
            case STD_EMIT_MBEAN_NBS: {
                // A StandardEmitterMBean that delegates to a NotificationBroadcasterSupport
                Broadcaster broadcaster = new Broadcaster();
                mbean = new StandardEmitterMBean(
                        broadcaster, BroadcasterMBean.class, wrappedVisOpts,
                        broadcaster);
                break;
            }
            case STD_EMIT_MBEAN_INJ: {
                // A StandardEmitterMBean that delegates to an object that injects
                // SendNotification
                SendNotif sendNotif = new SendNotif();
                mbean = new StandardEmitterMBean(
                        sendNotif, BroadcasterMBean.class, wrappedVisOpts,
                        null);
                break;
            }
            case STD_MBEAN_SUB_NBS:
                // A subclass of StandardMBean that implements NotificationBroadcaster
                mbean = new StdBroadcaster();
                break;
            case STD_MBEAN_SUB_INJ:
                // A subclass of StandardMBean that injects SendNotification
                mbean = new StdSendNotif();
                break;
            case STD_EMIT_MBEAN_SUB:
                // A subclass of StandardEmitterMBean
                mbean = new StdEmitterBroadcaster();
                break;
            case STD_EMIT_MBEAN_SUB_INJ:
                // A subclass of StandardEmitterMBean that injects SendNotification
                // (which is a rather strange thing to do and probably a user
                // misunderstanding but we should do the right thing anyway).
                mbean = new StdEmitterSendNotif();
                break;
            default:
                throw new AssertionError();
            }

            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

            final ObjectName name = new ObjectName("a:type=Sender");
            mbs.registerMBean(mbean, name);
            boolean isBroadcaster = mbs.isInstanceOf(
                    name, NotificationBroadcaster.class.getName());
            assertEquals("JMX.isNotificationSource(mbean)",
                    true, JMX.isNotificationSource(mbean));
            assertEquals("isInstanceOf(NotificationBroadcaster)",
                    true, isBroadcaster);
            MBeanNotificationInfo[] mbnis =
                    mbs.getMBeanInfo(name).getNotifications();
            assertEquals("MBeanNotificationInfo not empty",
                    true, (mbnis.length > 0));

            mbs.unregisterMBean(name);
        }
    }

    private static void assertEquals(String what, Object expect, Object actual) {
        if (equal(expect, actual))
            System.out.println("OK: " + what + " = " + expect);
        else
            fail(what + " should be " + expect + ", is " + actual);
    }

    private static boolean equal(Object x, Object y) {
        if (x == y)
            return true;
        if (x == null || y == null)
            return false;
        return x.equals(y);
    }

    private static void fail(String why) {
        failure = why;
        System.out.println("FAIL: " + why);
    }
}