jdk/test/javax/management/query/QueryNotifFilterTest.java
author xdono
Wed, 02 Jul 2008 12:55:45 -0700
changeset 715 f16baef3a20e
parent 287 bff5501b2a02
child 1004 5ba8217eb504
permissions -rw-r--r--
6719955: Update copyright year Summary: Update copyright year for files that have been modified in 2008 Reviewed-by: ohair, tbell

/*
 * 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 QueryNotifFilterTest
 * @bug 6610917
 * @summary Test the QueryNotificationFilter class
 * @author Eamonn McManus
 */

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeChangeNotification;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.QueryEval;
import javax.management.QueryExp;
import javax.management.QueryNotificationFilter;

public class QueryNotifFilterTest {
    private static class Case {
        final Notification notif;
        final QueryExp query;
        final boolean expect;
        final Class<? extends Notification> notifClass;
        Case(Notification notif, String query, boolean expect) {
            this(notif, query, notif.getClass(), expect);
        }
        Case(Notification notif, String query,
                Class<? extends Notification> notifClass, boolean expect) {
            this(notif, Query.fromString(query), notifClass, expect);
        }
        Case(Notification notif, QueryExp query, boolean expect) {
            this(notif, query, notif.getClass(), expect);
        }
        Case(Notification notif, QueryExp query,
                Class<? extends Notification> notifClass, boolean expect) {
            this.notif = notif;
            this.query = query;
            this.expect = expect;
            this.notifClass = notifClass;
        }
    }

    /* In principle users can create their own implementations of QueryExp
     * and use them with QueryNotificationFilter.  If they do so, then
     * they can call any MBeanServer method.  Not all of those methods
     * will work with the special MBeanServer we concoct to analyze a
     * Notification, but some will, including some that are not called
     * by the standard queries.  So we check each of those cases too.
     */
    private static class ExoticCase {
        final Notification trueNotif;
        final Notification falseNotif;
        final QueryExp query;
        ExoticCase(Notification trueNotif, Notification falseNotif, QueryExp query) {
            this.trueNotif = trueNotif;
            this.falseNotif = falseNotif;
            this.query = query;
        }
    }

    private static abstract class ExoticQuery
            extends QueryEval implements QueryExp {
        private final String queryString;
        ExoticQuery(String queryString) {
            this.queryString = queryString;
        }
        abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception;
        @Override
        public boolean apply(ObjectName name) {
            try {
                return apply(getMBeanServer(), name);
            } catch (Exception e) {
                e.printStackTrace(System.out);
                return false;
            }
        }
        @Override
        public String toString() {
            return queryString;
        }
    }

    private static ObjectName makeObjectName(String s) {
        try {
            return new ObjectName(s);
        } catch (MalformedObjectNameException e) {
            throw new RuntimeException(e);
        }
    }

    public static class CustomNotification extends Notification {
        public CustomNotification(String type, Object source, long seqNo) {
            super(type, source, seqNo);
        }

        public String getName() {
            return "claude";
        }

        public boolean isInteresting() {
            return true;
        }
    }

    private static final Notification simpleNotif =
            new Notification("mytype", "source", 0L);
    private static final Notification attrChangeNotif =
            new AttributeChangeNotification(
                    "x", 0L, 0L, "msg", "AttrName", "int", 2, 3);
    private static final ObjectName testObjectName = makeObjectName("a:b=c");
    private static final Notification sourcedNotif =
            new Notification("mytype", testObjectName, 0L);
    private static final Notification customNotif =
            new CustomNotification("mytype", testObjectName, 0L);

    private static final Case[] testCases = {
        new Case(simpleNotif, "Type = 'mytype'", true),
        new Case(simpleNotif, "Type = 'mytype'",
                Notification.class, true),
        new Case(simpleNotif, "Type = 'mytype'",
                AttributeChangeNotification.class, false),
        new Case(simpleNotif, "Type != 'mytype'", false),
        new Case(simpleNotif, "Type = 'somethingelse'", false),
        new Case(attrChangeNotif, "AttributeName = 'AttrName'", true),
        new Case(attrChangeNotif,
                "instanceof 'javax.management.AttributeChangeNotification'",
                true),
        new Case(attrChangeNotif,
                "instanceof 'javax.management.Notification'",
                true),
        new Case(attrChangeNotif,
                "instanceof 'javax.management.relation.MBeanServerNotification'",
                false),
        new Case(attrChangeNotif,
                "class = 'javax.management.AttributeChangeNotification'",
                true),
        new Case(attrChangeNotif,
                "javax.management.AttributeChangeNotification#AttributeName = 'AttrName'",
                true),
        new Case(sourcedNotif,
                testObjectName,
                true),
        new Case(sourcedNotif,
                makeObjectName("a*:b=*"),
                true),
        new Case(sourcedNotif,
                makeObjectName("a*:c=*"),
                false),
        new Case(customNotif, "Name = 'claude'", true),
        new Case(customNotif, "Name = 'tiddly'", false),
        new Case(customNotif, "Interesting = true", true),
        new Case(customNotif, "Interesting = false", false),
    };

    private static final ExoticCase[] exoticTestCases = {
        new ExoticCase(
                simpleNotif, new Notification("notmytype", "source", 0L),
                new ExoticQuery("getAttributes") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        List<Attribute> attrs = mbs.getAttributes(
                                name, new String[] {"Type", "Source"}).asList();
                        return (attrs.get(0).equals(new Attribute("Type", "mytype")) &&
                                attrs.get(1).equals(new Attribute("Source", "source")));
                    }
                }),
        new ExoticCase(
                new Notification("mytype", "source", 0L) {},
                simpleNotif,
                new ExoticQuery("getClassLoaderFor") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        return (mbs.getClassLoaderFor(name) ==
                                this.getClass().getClassLoader());
                    }
                }),
        new ExoticCase(
                sourcedNotif, simpleNotif,
                new ExoticQuery("getDomains") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        return Arrays.equals(mbs.getDomains(),
                                new String[] {testObjectName.getDomain()});
                    }
                }),
        new ExoticCase(
                simpleNotif, attrChangeNotif,
                new ExoticQuery("getMBeanInfo") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        MBeanInfo mbi = mbs.getMBeanInfo(name);
                        // If we ever add a constructor to Notification then
                        // we will have to change the 4 below.
                        if (mbi.getOperations().length > 0 ||
                                mbi.getConstructors().length != 4 ||
                                mbi.getNotifications().length > 0)
                            return false;
                        Set<String> expect = new HashSet<String>(
                            Arrays.asList(
                                "Class", "Message", "SequenceNumber", "Source",
                                "TimeStamp", "Type", "UserData"));
                        Set<String> actual = new HashSet<String>();
                        for (MBeanAttributeInfo mbai : mbi.getAttributes())
                            actual.add(mbai.getName());
                        return actual.equals(expect);
                    }
                }),
        new ExoticCase(
                simpleNotif, attrChangeNotif,
                new ExoticQuery("getObjectInstance") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        ObjectInstance oi = mbs.getObjectInstance(name);
                        return oi.getClassName().equals(Notification.class.getName());
                    }
                }),
        new ExoticCase(
                sourcedNotif, simpleNotif,
                new ExoticQuery("queryNames") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        Set<ObjectName> names = mbs.queryNames(null,
                                Query.eq(Query.attr("Type"), Query.value("mytype")));
                        return names.equals(Collections.singleton(testObjectName));
                    }
                }),
        new ExoticCase(
                sourcedNotif, simpleNotif,
                new ExoticQuery("queryMBeans") {
                    boolean apply(MBeanServer mbs, ObjectName name)
                            throws Exception {
                        Set<ObjectInstance> insts = mbs.queryMBeans(null,
                                Query.eq(Query.attr("Type"), Query.value("mytype")));
                        if (insts.size() != 1)
                            return false;
                        ObjectInstance inst = insts.iterator().next();
                        return (inst.getObjectName().equals(testObjectName) &&
                                inst.getClassName().equals(Notification.class.getName()));
                    }
                }),
    };

    private static enum Test {
        QUERY_EXP("query"), STRING("string"), STRING_PLUS_CLASS("string with class");
        private final String name;
        Test(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return name;
        }
    }

    public static void main(String[] args) throws Exception {
        boolean allok = true;
        for (Case testCase : testCases) {
            for (Test test : Test.values()) {
                QueryNotificationFilter nf;
                String queryString;
                switch (test) {
                case QUERY_EXP: {
                    QueryExp inst = Query.isInstanceOf(
                            Query.value(testCase.notifClass.getName()));
                    QueryExp and = Query.and(inst, testCase.query);
                    queryString = Query.toString(and);
                    nf = new QueryNotificationFilter(and);
                    break;
                }
                case STRING: {
                    String s = "instanceof '" + testCase.notifClass.getName() + "'";
                    queryString = s + " and " + Query.toString(testCase.query);
                    nf = new QueryNotificationFilter(queryString);
                    break;
                }
                case STRING_PLUS_CLASS:
                    queryString = null;
                    nf = new QueryNotificationFilter(
                            testCase.notifClass, Query.toString(testCase.query));
                    break;
                default:
                    throw new AssertionError();
                }
                boolean accept = nf.isNotificationEnabled(testCase.notif);
                if (queryString != null) {
                    queryString = Query.toString(Query.fromString(queryString));
                    if (!queryString.equals(Query.toString(nf.getQuery()))) {
                        System.out.println("FAIL: query string mismatch: expected " +
                                "\"" + queryString + "\", got \"" +
                                Query.toString(nf.getQuery()));
                        allok = false;
                    }
                }
                boolean ok = (accept == testCase.expect);
                System.out.println((ok ? "pass" : "FAIL") + ": " +
                        testCase.query + " (" + test + ")");
                allok &= ok;
            }
        }
        for (ExoticCase testCase : exoticTestCases) {
            NotificationFilter nf = new QueryNotificationFilter(testCase.query);
            for (boolean expect : new boolean[] {true, false}) {
                Notification n = expect ? testCase.trueNotif : testCase.falseNotif;
                boolean accept = nf.isNotificationEnabled(n);
                boolean ok = (accept == expect);
                System.out.println((ok ? "pass" : "FAIL") + ": " +
                        testCase.query + ": " + n);
                allok &= ok;
            }
        }
        if (!allok)
            throw new Exception("TEST FAILED");
    }
}