jdk/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java
changeset 1156 bbc2d15aaf7a
child 1222 78e3d021d528
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,322 @@
+/*
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package com.sun.jmx.interceptor;
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.MBeanInstantiator;
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.namespace.DomainInterceptor;
+import java.util.Queue;
+import java.util.Set;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.namespace.JMXDomain;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+/**
+ * A dispatcher that dispatch incoming MBeanServer requests to
+ * DomainInterceptors.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+//
+// See comments in  DispatchInterceptor.
+//
+class DomainDispatchInterceptor
+        extends DispatchInterceptor<DomainInterceptor, JMXDomain> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    private static final ObjectName ALL_DOMAINS =
+            JMXDomain.getDomainObjectName("*");
+
+
+    /**
+     *  A QueryInterceptor that perform & aggregates queries spanning several
+     *  domains.
+     */
+    final static class AggregatingQueryInterceptor extends QueryInterceptor {
+
+        private final DomainDispatchInterceptor parent;
+        AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) {
+            super(dispatcher.localNamespace);
+            parent = dispatcher;
+        }
+
+        /**
+         * Perform queryNames or queryMBeans, depending on which QueryInvoker
+         * is passed as argument. This is closures without closures.
+         **/
+        @Override
+        <T> Set<T> query(ObjectName pattern, QueryExp query,
+                QueryInvoker<T> invoker, MBeanServer localNamespace) {
+            final Set<T> local = invoker.query(localNamespace, pattern, query);
+
+            // Add all matching MBeans from local namespace.
+            final Set<T> res = Util.cloneSet(local);
+
+            final boolean all = (pattern == null ||
+                    pattern.getDomain().equals("*"));
+            if (pattern == null) pattern = ObjectName.WILDCARD;
+
+            final String domain = pattern.getDomain();
+
+            // If there's no domain pattern, just include the pattern's domain.
+            // Otherwiae, loop over all virtual domains (parent.getKeys()).
+            final String[] keys =
+                (pattern.isDomainPattern() ?
+                    parent.getKeys() : new String[]{domain});
+
+            // Add all matching MBeans from each virtual domain
+            //
+            for (String key : keys) {
+                // Only invoke those virtual domain which are selected
+                // by the domain pattern
+                //
+                if (!all && !Util.isDomainSelected(key, domain))
+                    continue;
+
+                try {
+                    final MBeanServer mbs = parent.getInterceptor(key);
+
+                    // mbs can be null if the interceptor was removed
+                    // concurrently...
+                    // See handlerMap and getKeys() in DispatchInterceptor
+                    //
+                    if (mbs == null) continue;
+
+                    // If the domain is selected, we can replace the pattern
+                    // by the actual domain. This is safer if we want to avoid
+                    // a domain (which could be backed up by an MBeanServer) to
+                    // return names from outside the domain.
+                    // So instead of asking the domain handler for "foo" to
+                    // return all names which match "?o*:type=Bla,*" we're
+                    // going to ask it to return all names which match
+                    // "foo:type=Bla,*"
+                    //
+                    final ObjectName subPattern = pattern.withDomain(key);
+                    res.addAll(invoker.query(mbs, subPattern, query));
+                } catch (Exception x) {
+                    LOG.finest("Ignoring exception " +
+                            "when attempting to query namespace "+key+": "+x);
+                    continue;
+                }
+            }
+            return res;
+        }
+    }
+
+    private final DefaultMBeanServerInterceptor localNamespace;
+    private final String mbeanServerName;
+    private final MBeanServerDelegate delegate;
+
+    /**
+     * Creates a DomainDispatchInterceptor with the specified
+     * repository instance.
+     *
+     * @param outer A pointer to the MBeanServer object that must be
+     *        passed to the MBeans when invoking their
+     *        {@link javax.management.MBeanRegistration} interface.
+     * @param delegate A pointer to the MBeanServerDelegate associated
+     *        with the new MBeanServer. The new MBeanServer must register
+     *        this MBean in its MBean repository.
+     * @param instantiator The MBeanInstantiator that will be used to
+     *        instantiate MBeans and take care of class loading issues.
+     * @param repository The repository to use for this MBeanServer
+     */
+    public DomainDispatchInterceptor(MBeanServer         outer,
+                            MBeanServerDelegate delegate,
+                            MBeanInstantiator   instantiator,
+                            Repository          repository,
+                            NamespaceDispatchInterceptor namespaces)  {
+           localNamespace = new DefaultMBeanServerInterceptor(outer,
+                   delegate, instantiator,repository,namespaces);
+           mbeanServerName = Util.getMBeanServerSecurityName(delegate);
+           this.delegate = delegate;
+    }
+
+    final boolean isLocalHandlerNameFor(String domain,
+            ObjectName handlerName) {
+        if (domain == null) return true;
+        return handlerName.getDomain().equals(domain) &&
+               JMXDomain.TYPE_ASSIGNMENT.equals(
+               handlerName.getKeyPropertyListString());
+    }
+
+    @Override
+    void validateHandlerNameFor(String key, ObjectName name) {
+        super.validateHandlerNameFor(key,name);
+        final String[] domains = localNamespace.getDomains();
+        for (int i=0;i<domains.length;i++) {
+            if (domains[i].equals(key))
+                throw new IllegalArgumentException("domain "+key+
+                        " is not empty");
+        }
+    }
+
+    @Override
+    final MBeanServer getInterceptorOrNullFor(ObjectName name) {
+        if (name == null) return localNamespace;
+        final String domain = name.getDomain();
+        if (domain.endsWith(NAMESPACE_SEPARATOR)) return localNamespace;
+        if (domain.contains(NAMESPACE_SEPARATOR)) return null;
+        final String localDomain = domain;
+        if (isLocalHandlerNameFor(localDomain,name)) {
+            LOG.finer("dispatching to local namespace");
+            return localNamespace;
+        }
+        final DomainInterceptor ns = getInterceptor(localDomain);
+        if (ns == null) {
+            if (LOG.isLoggable(Level.FINER)) {
+                LOG.finer("dispatching to local namespace: " + localDomain);
+            }
+            return getNextInterceptor();
+        }
+        if (LOG.isLoggable(Level.FINER)) {
+            LOG.finer("dispatching to domain: " + localDomain);
+        }
+        return ns;
+    }
+
+    private boolean multipleQuery(ObjectName pattern) {
+        if (pattern == null) return true;
+        if (pattern.isDomainPattern()) return true;
+
+        try {
+            // This is a bit of a hack. If there's any chance that a JMXDomain
+            // MBean name is selected by the given pattern then we must include
+            // the local namespace in our search.
+            // Returning true will have this effect.
+            if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain())))
+                return true;
+        } catch (MalformedObjectNameException x) {
+            // should not happen
+            throw new IllegalArgumentException(String.valueOf(pattern), x);
+        }
+        return false;
+    }
+
+    @Override
+    final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
+
+        // Check if we need to aggregate.
+        if (multipleQuery(pattern))
+            return new AggregatingQueryInterceptor(this);
+
+        // We don't need to aggregate: do the "simple" thing...
+        final String domain = pattern.getDomain();
+
+        // Do we have a virtual domain?
+        final DomainInterceptor ns = getInterceptor(domain);
+        if (ns != null) {
+            if (LOG.isLoggable(Level.FINER))
+                LOG.finer("dispatching to domain: " + domain);
+            return new QueryInterceptor(ns);
+        }
+
+        // We don't have a virtual domain. Send to local domains.
+        if (LOG.isLoggable(Level.FINER))
+             LOG.finer("dispatching to local namespace: " + domain);
+        return new QueryInterceptor(localNamespace);
+    }
+
+    @Override
+    final ObjectName getHandlerNameFor(String key)
+        throws MalformedObjectNameException {
+        return JMXDomain.getDomainObjectName(key);
+    }
+
+    @Override
+    final public String getHandlerKey(ObjectName name) {
+        return name.getDomain();
+    }
+
+    @Override
+    final DomainInterceptor createInterceptorFor(String key,
+            ObjectName name, JMXDomain handler,
+            Queue<Runnable> postRegisterQueue) {
+        final DomainInterceptor ns =
+                new DomainInterceptor(mbeanServerName,handler,key);
+        ns.addPostRegisterTask(postRegisterQueue, delegate);
+        if (LOG.isLoggable(Level.FINER)) {
+            LOG.finer("DomainInterceptor created: "+ns);
+        }
+        return ns;
+    }
+
+    @Override
+    final void interceptorReleased(DomainInterceptor interceptor,
+            Queue<Runnable> postDeregisterQueue) {
+        interceptor.addPostDeregisterTask(postDeregisterQueue, delegate);
+    }
+
+    @Override
+    final DefaultMBeanServerInterceptor getNextInterceptor() {
+        return localNamespace;
+    }
+
+    /**
+     * Returns the list of domains in which any MBean is currently
+     * registered.
+     */
+    @Override
+    public String[] getDomains() {
+        // A JMXDomain is registered in its own domain.
+        // Therefore, localNamespace.getDomains() contains all domains.
+        // In addition, localNamespace will perform the necessary
+        // MBeanPermission checks for getDomains().
+        //
+        return localNamespace.getDomains();
+    }
+
+    /**
+     * Returns the number of MBeans registered in the MBean server.
+     */
+    @Override
+    public Integer getMBeanCount() {
+        int count = getNextInterceptor().getMBeanCount().intValue();
+        final String[] keys = getKeys();
+        for (String key:keys) {
+            final MBeanServer mbs = getInterceptor(key);
+            if (mbs == null) continue;
+            count += mbs.getMBeanCount().intValue();
+        }
+        return Integer.valueOf(count);
+    }
+}