jdk/test/javax/management/namespace/QueryNamesTest.java
author dfuchs
Thu, 04 Sep 2008 14:46:36 +0200
changeset 1156 bbc2d15aaf7a
child 1227 4546977d0d66
permissions -rw-r--r--
5072476: RFE: support cascaded (federated) MBean Servers 6299231: Add support for named MBean Servers Summary: New javax.management.namespace package. Reviewed-by: emcmanus

/*
 * Copyright 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 QueryNamesTest.java 1.4
 * @summary Test how queryNames works with Namespaces.
 * @author Daniel Fuchs
 * @run clean QueryNamesTest Wombat WombatMBean
 * @run build QueryNamesTest Wombat WombatMBean
 * @run main QueryNamesTest
 */


import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Logger;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.namespace.JMXNamespace;
import javax.management.namespace.JMXNamespaces;

/**
 * Class QueryNamesTest
 * @author Sun Microsystems, 2005 - All rights reserved.
 */
public class QueryNamesTest {

    /**
     * A logger for this class.
     **/
    private static final Logger LOG =
            Logger.getLogger(QueryNamesTest.class.getName());

    public static class LocalNamespace
            extends JMXNamespace {

        private static MBeanServer check(MBeanServer server) {
            if (server == null)
                throw new IllegalArgumentException("MBeanServer can't be null");
            return server;
        }

        public LocalNamespace() {
            this(MBeanServerFactory.createMBeanServer());
        }

        public LocalNamespace(MBeanServer server) {
            super(check(server));
        }


        public static String add(MBeanServerConnection server,
                String nspath)
                throws IOException, JMException {
            server.createMBean(LocalNamespace.class.getName(),
                    JMXNamespaces.getNamespaceObjectName(nspath));
            return nspath;
        }
    }

    /** Creates a new instance of QueryNamesTest */
    public QueryNamesTest() {
    }

    private static String[] namespaces = {
        "greg", "greg//chichille", "greg//chichille//petard",
        "greg//alambic", "greg//alambic//canette",
        "greg//chichille/virgule", "greg//chichille/funeste",
        "greg//chichille/virgule//bidouble",
        "greg//chichille/virgule//bi/double",
        "fran", "fran//gast", "fran//gast//gaf",
        "fran//longtar", "fran//longtar//parcmetre"
    };

    private static void createNamespaces(MBeanServer server) throws Exception {
        final LinkedList<String> all = new LinkedList<String>();
        try {
            for (String ns : namespaces)
                all.addFirst(LocalNamespace.add(server,ns));
        } catch (Exception e) {
            removeNamespaces(server,all.toArray(new String[all.size()]));
            throw e;
        }
    }

    // Dummy test that checks that all JMXNamespaces are registered,
    // but are not returned by queryNames("*:*");
    //
    private static void checkRegistration(MBeanServer server)
        throws Exception {
        final Set<ObjectName> handlerNames = new HashSet<ObjectName>(namespaces.length);
        for (String ns : namespaces)
            handlerNames.add(JMXNamespaces.getNamespaceObjectName(ns));
        for (ObjectName nh : handlerNames) // check handler registration
            if (!server.isRegistered(nh))
                throw new InstanceNotFoundException("handler "+nh+
                        " is not registered");

        // global: queryNames("*:*") from top level
        final Set<ObjectName> all1 = server.queryNames(null,null);
        final Set<ObjectName> all2 = server.queryNames(ObjectName.WILDCARD,null);
        if (!all1.equals(all2))
            throw new Exception("queryNames(*:*) != queryNames(null)");
        final Set<ObjectName> common = new HashSet<ObjectName>(all1);
        common.retainAll(handlerNames);

        final Set<ObjectName> ref = new HashSet<ObjectName>();
        for (String ns : namespaces) {
            if (!ns.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
                ref.add(JMXNamespaces.getNamespaceObjectName(ns));
        }
        if (!common.equals(ref)) {
            throw new Exception("some handler names were not returned by " +
                    "wildcard query - only returned: "+common+
                    ", expected: "+ref);
        }

        // for each namespace: queryNames("<namespace>//*:*");
        for (String ns : namespaces) {
            final ObjectName pattern = new ObjectName(ns+
                    JMXNamespaces.NAMESPACE_SEPARATOR+"*:*");
            final Set<ObjectName> all4 =
                    server.queryNames(pattern,null);
            final Set<ObjectName> common4 = new HashSet<ObjectName>(all4);
            common4.retainAll(handlerNames);

            final Set<ObjectName> ref4 = new HashSet<ObjectName>();
            for (String ns2 : namespaces) {
                if (! ns2.startsWith(ns+JMXNamespaces.NAMESPACE_SEPARATOR))
                    continue;
                if (!ns2.substring(ns.length()+
                        JMXNamespaces.NAMESPACE_SEPARATOR.length()).
                        contains(JMXNamespaces.NAMESPACE_SEPARATOR))
                    ref4.add(JMXNamespaces.getNamespaceObjectName(ns2));
            }
            if (!common4.equals(ref4)) {
                throw new Exception("some handler names were not returned by " +
                    "wildcard query on "+pattern+" - only returned: "+common4+
                    ", expected: "+ref4);
            }
        }
    }

    // Make a Map<parent, direct children>
    private static Map<String,Set<String>> makeNsTree(String[] nslist) {
        final Map<String,Set<String>> nsTree =
                new LinkedHashMap<String,Set<String>>(nslist.length);
        for (String ns : nslist) {
            if (nsTree.get(ns) == null)
                nsTree.put(ns,new LinkedHashSet<String>());
            final String[] elts = ns.split(JMXNamespaces.NAMESPACE_SEPARATOR);
            int last = ns.lastIndexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
            if (last<0) continue;
            while (last > 0 && ns.charAt(last-1) == '/') last--;
            final String parent = ns.substring(0,last);
            if (nsTree.get(parent) == null)
                nsTree.put(parent,new LinkedHashSet<String>());
            nsTree.get(parent).add(ns);
        }
        return nsTree;
    }

    private static class Rigolo {
        final static String[] ones = { "a", "e", "i", "o", "u", "y", "ai", "oo",
        "ae", "ey", "ay", "oy", "au", "ou", "eu", "oi", "ei", "ea"};
        final static String[] twos = { "b", "bz", "c", "cz", "ch",
        "ct", "ck", "cs", "d", "ds", "f",  "g", "gh", "h", "j", "k", "l", "m",
        "n", "p", "ps", "q", "r", "s", "sh", "t", "v", "w", "x",
        "z"};
        final static String[] threes = {"rr","tt","pp","ss","dd","ff","ll", "mm", "nn",
        "zz", "cc", "bb"};
        final static String[] fours = {"x", "s", "ght", "cks", "rt", "rts", "ghts", "bs",
          "ts", "gg" };
        final static String[] fives = { "br", "bl", "cr", "cn", "cth", "dr",
        "fr", "fl", "cl", "chr",  "gr", "gl", "kr", "kh", "pr", "pl", "ph",
        "rh", "sr", "tr", "vr"};

        private Random rg = new Random();

        private String next(String[] table) {
            return table[rg.nextInt(table.length)];
        }

        public String nextName(int max) {
            final Random rg = new Random();
            final int nl = 3 + rg.nextInt(max);
            boolean begin = rg.nextBoolean();
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < nl ; j++) {
                if (begin) {
                    sb.append(next(ones));
                } else if (j > 0 && j < nl-1 && rg.nextInt(4)==0) {
                    sb.append(next(threes));
                } else if (j < nl-1 && rg.nextInt(3)==0) {
                    sb.append(next(fives));
                } else {
                    sb.append(next(twos));
                }
                begin = !begin;
            }
            if (!begin && rg.nextInt(2)==0)
                sb.append(next(fours));
            return sb.toString();
        }

        private ObjectName getWombatName(String ns, String domain, String name)
            throws MalformedObjectNameException {
            String d = domain;
            if (ns != null && !ns.equals(""))
                d = ns + JMXNamespaces.NAMESPACE_SEPARATOR + domain;
            return new ObjectName(d+":type=Wombat,name="+name);
        }

        public Set<ObjectName> nextWombats(String ns)
            throws MalformedObjectNameException  {
            final int dcount = 1 + rg.nextInt(5);
            final Set<ObjectName> wombats = new HashSet<ObjectName>();
            for (int i = 0; i < dcount ; i++) {
                final String d = nextName(7);
                final int ncount = 5 + rg.nextInt(20);
                for (int j = 0 ; j<ncount; j++) {
                    final String n = nextName(5);
                    wombats.add(getWombatName(ns,d,n));
                }
            }
            return wombats;
        }
    }

    public static void checkNsQuery(MBeanServer server)
        throws Exception {
        final Map<String,Set<String>> nsTree = makeNsTree(namespaces);
        final Random rg = new Random();
        final Rigolo rigolo = new Rigolo();
        for (String ns : namespaces) {
            final ObjectName name = JMXNamespaces.getNamespaceObjectName(ns);
            final String[] doms =
                    (String[])server.getAttribute(name,"Domains");
            final Set<String> subs = new HashSet<String>();
            for (String d : doms) {
                if (d.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
                    subs.add(ns+JMXNamespaces.NAMESPACE_SEPARATOR+d.substring(0,
                            d.length()-JMXNamespaces.NAMESPACE_SEPARATOR.length()));
                }
            }

            final Set<String> expectNs = new HashSet<String>(nsTree.get(ns));

            if (! subs.containsAll(expectNs))
                throw new Exception("getDomains didn't return all namespaces: "+
                        "returned="+subs+", expected="+expectNs);
            if (! expectNs.containsAll(subs))
                throw new Exception("getDomains returned additional namespaces: "+
                        "returned="+subs+", expected="+expectNs);

            final Set<ObjectName> nsNames = server.queryNames(
                    new ObjectName(ns+
                    JMXNamespaces.NAMESPACE_SEPARATOR+"*"+
                    JMXNamespaces.NAMESPACE_SEPARATOR+":*"),null);

            final Set<ObjectName> expect =
                    new HashSet<ObjectName>(expectNs.size());
            for (String sub : expectNs) {
                expect.add(JMXNamespaces.getNamespaceObjectName(sub));
            }

            if (! nsNames.containsAll(expect))
                throw new Exception("queryNames didn't return all namespaces: "+
                        "returned="+nsNames+", expected="+expect);
            if (! expect.containsAll(nsNames))
                throw new Exception("getDomains returned additional namespaces: "+
                        "returned="+nsNames+", expected="+expect);

        }
    }

    private static void addWombats(MBeanServer server, Set<ObjectName> names)
        throws Exception {
        for (ObjectName on : names) {
            if (! server.isRegistered(on)) {
                server.createMBean(Wombat.class.getName(),on);
                System.out.println("A new wombat is born: "+on);
            }
        }
    }

    private static void addWombats(MBeanServer server,
             Map<String,Set<ObjectName>> wombats)
        throws Exception {
        for (String ns : wombats.keySet()) {
            addWombats(server,wombats.get(ns));
        }
    }

    private static Map<String,Set<ObjectName>> nameWombats()
        throws Exception {
        final Rigolo rigolo = new Rigolo();
        final Map<String,Set<ObjectName>> wombats =
                new HashMap<String,Set<ObjectName>>(namespaces.length);

        for (String ns : namespaces) {
            wombats.put(ns,rigolo.nextWombats(ns));
        }
        wombats.put("",rigolo.nextWombats(""));
        return wombats;
    }

    private static boolean removeWombats(MBeanServer server,
            Map<String,Set<ObjectName>> wombats) {
        boolean res = true;
        for (String ns : wombats.keySet()) {
            res = res && removeWombats(server,wombats.get(ns));
        }
        return res;
    }

    private static boolean removeWombats(MBeanServer server,
            Set<ObjectName> wombats) {
        boolean res = true;
        for (ObjectName on : wombats) {
            try {
                if (server.isRegistered(on))
                    server.unregisterMBean(on);
            } catch (Exception x) {
                res = false;
                System.out.println("Failed to remove "+on+": "+x);
            }
        }
        return res;
    }

    public static void main(String[] args)
        throws Exception {
        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        Map<String,Set<ObjectName>>  wombats = nameWombats();
        createNamespaces(server);
        try {
            addWombats(server,wombats);
            System.out.println("MBeans: " +server.getMBeanCount());
            System.out.println("Visible: " +server.queryNames(null,null).size());
            System.out.println("Domains: " +Arrays.asList(server.getDomains()));
            checkRegistration(server);
            checkNsQuery(server);
        } finally {
            boolean res = true;
            res = res && removeWombats(server, wombats);
            if (!res)
                throw new RuntimeException("failed to cleanup some namespaces");
        }

    }

    private static boolean removeNamespaces(MBeanServer server) {
        final List<String> l = Arrays.asList(namespaces);
        Collections.reverse(l);
        return removeNamespaces(server, l.toArray(new String[namespaces.length]));
    }

    private static boolean removeNamespaces(MBeanServer server, String[] t) {
        boolean success = true;
        for (String ns : t) {
            try {
                server.unregisterMBean(JMXNamespaces.getNamespaceObjectName(ns));
            } catch (Exception x) {
                System.out.println("failed to remove namespace: "+ ns);
                success = false;
            }
        }
        return success;
    }

}