jdk/src/share/classes/javax/management/namespace/JMXDomain.java
changeset 1156 bbc2d15aaf7a
child 1222 78e3d021d528
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/management/namespace/JMXDomain.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,382 @@
+/*
+ * 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 javax.management.namespace;
+
+import java.io.IOException;
+import javax.management.ListenerNotFoundException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+/**
+ * A special {@link JMXNamespace} that can handle part of
+ * the MBeanServer local name space.
+ * <p>
+ * A {@code JMXDomain} makes a domain <i>X</i> of a
+ * {@linkplain #getSourceServer() source MBean server} appear in the same domain
+ * <i>X</i> of a containing {@code MBeanServer} in which the
+ * {@code JMXDomain} MBean {@linkplain #getMBeanServer() is registered}.
+ * </p>
+ * <p>
+ * The JMX infrastructure of the containing {@code MBeanServer} takes care of
+ * routing all calls to MBeans whose names have domain <i>X</i> to the
+ * {@linkplain #getSourceServer() source MBean server} exported by the
+ * {@code JMXDomain} MBean in charge of domain <i>X</i>.
+ * </p>
+ * <p>
+ * The {@linkplain #getSourceServer() source MBean server} of a {@code JMXDomain}
+ * can, but need not be a regular {@code MBeanServer} created through
+ * the {@link javax.management.MBeanServerFactory}. It could also be,
+ * for instance, an instance of a subclass of {@link MBeanServerSupport},
+ * or a custom object implementing the {@link MBeanServer} interface.
+ * </p>
+ *
+ * <h4>Differences between {@code JMXNamespace} and {@code JMXDomain}</h4>
+ *
+ * <p>
+ * A {@code JMXDomain} is a special kind of {@code JMXNamespace}.
+ * A {@code JMXNamespace} such as {@code foo//} is triggered by an
+ * {@code ObjectName} that begins with the string {@code foo//}, for example
+ * {@code foo//bar:type=Baz}.  A {@code JMXDomain} such as {@code foo} is
+ * triggered by an {@code ObjectName} with that exact domain, for example
+ * {@code foo:type=Baz}.  A client can immediately see that an MBean is
+ * handled by a {@code JMXNamespace} because of the {@code //} in the name.
+ * A client cannot see whether a name such as {@code foo:type=Baz} is an
+ * ordinary MBean or is handled by a {@code JMXDomain}.
+ * </p>
+ *
+ * <p>
+ * A {@linkplain MBeanServer#queryNames query} on the containing {@code
+ * MBeanserver} will return all MBeans from the {@code JMXDomain} that match
+ * the query.  In particular, {@code queryNames(null, null)} will return all
+ * MBeans including those from {@code JMXDomain} domains.  On the other hand,
+ * a query will not include MBeans from a {@code JMXNamespace} unless the
+ * {@code ObjectName} pattern in the query starts with the name of that
+ * namespace.
+ * </p>
+ *
+ * <h4 id="security">Permission checks</h4>
+ *
+ * <p>
+ * When a JMXDomain MBean is registered in a containing
+ * MBean server created through the default {@link
+ * javax.management.MBeanServerBuilder}, and if a {@link
+ * SecurityManager SecurityManager} is
+ * {@linkplain System#getSecurityManager() present}, the containing MBeanServer will
+ * check an {@link javax.management.MBeanPermission} before invoking
+ * any method on the {@linkplain #getSourceServer() source MBeanServer} of the
+ * JMXDomain.
+ * </p>
+ *
+ * <p>First, if there is no security manager ({@link
+ * System#getSecurityManager()} is null), that containing
+ * {@code MBeanServer} is free not to make any checks.
+ * </p>
+ *
+ * <p>
+ * Assuming that there is a security manager, or that the
+ * implementation chooses to make checks anyway, the containing
+ * {@code MBeanServer} will perform
+ * {@link javax.management.MBeanPermission MBeanPermission} checks
+ * for access to the MBeans in domain <i>X</i> handled by a {@code JMXDomain}
+ * in the same way that it would do for MBeans registered in its own local
+ * repository, and as <a href="../MBeanServer.html#security">described in
+ * the MBeanServer interface</a>, with the following exceptions:
+ * </p>
+ *
+ * <p>
+ * For those permissions that require a {@code className}, the
+ * <code>className</code> is the
+ * string returned by {@link #getSourceServer() getSourceServer()}.
+ * {@link MBeanServer#getObjectInstance(ObjectName)
+ * getObjectInstance(mbeanName)}.
+ * {@link javax.management.ObjectInstance#getClassName() getClassName()},
+ * except for {@code createMBean} and {@code registerMBean} operations,
+ * for which the permission checks are performed as follows:
+ * </p>
+ * <ul>
+ * <li><p>For {@code createMBean} operations, the {@code className} of the
+ * permission you need is the {@code className} passed as first parameter
+ * to {@code createMBean}.</p>
+ *
+ * <li><p>For {@code registerMBean} operations, the {@code className} of the
+ * permission you need is the name of the class of the mbean object, as
+ * returned by {@code mbean.getClass().getClassName()}, where
+ * {@code mbean} is the mbean reference passed as first parameter
+ * to {@code registerMBean}.</p>
+ *
+ * <li><p>In addition, for {@code createMBean} and {@code registerMBean}, the
+ * permission you need is checked with the {@linkplain ObjectName object name} of
+ * the mbean that is passed as second parameter to the {@code createMBean} or
+ * {@code registerMBean} operation.
+ * </p>
+ *
+ * <li><p>Contrarily to what is done for regular MBeans registered in the
+ *     MBeanServer local repository, the containing MBeanServer will not
+ *     check the {@link javax.management.MBeanTrustPermission#MBeanTrustPermission(String)
+ *     MBeanTrustPermission("register")} against the protection domain
+ *     of the MBean's class. This check can be performed by the
+ *     {@linkplain #getSourceServer source MBean server} implementation,
+ *     if necessary.
+ * </p>
+ * </ul>
+ *
+ * <p>If a security check fails, the method throws {@link
+ * SecurityException}.</p>
+ *
+ * <p>For methods that can throw {@link InstanceNotFoundException},
+ * this exception is thrown for a non-existent MBean, regardless of
+ * permissions.  This is because a non-existent MBean has no
+ * <code>className</code>.</p>
+ *
+ * All these checks are performed by the containing {@code MBeanServer},
+ * before accessing the JMXDomain {@linkplain #getSourceServer source MBean server}.
+ * The implementation of the JMXDomain {@linkplain #getSourceServer source MBean
+ * server} is free to make any additional checks. In fact, if the JMXDomain
+ * {@linkplain #getSourceServer source MBean server} is an {@code MBeanServer}
+ * obtained through the {@link javax.management.MBeanServerFactory}, it will
+ * again make permission checks as described in the
+ * <a href="../MBeanServer.html#security">MBeanServer</a> interface.
+ *
+ * <p>See the <a href="../MBeanServer.html#security">MBeanServer</a> interface
+ * for more details on permission checks.</p>
+ *
+ * @since 1.7
+ */
+public class JMXDomain extends JMXNamespace {
+
+
+    /**
+     * This constant contains the value of the {@code type}
+     * key used in defining a standard JMXDomain MBean object name.
+     * By definition, a standard JMXDomain MBean object name must be of
+     * the form:
+     * <pre>
+     * {@code "<domain>:"}+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
+     * </pre>
+     */
+    public static final String TYPE = "JMXDomain";
+
+    /**
+     * This constant contains the value of the standard
+     * {@linkplain javax.management.ObjectName#getKeyPropertyListString() key
+     * property list string} for JMXDomain MBean object names.
+     * By definition, a standard JMXDomain MBean object name must be of
+     * the form:
+     * <pre>
+     * {@code <domain>}+":"+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
+     * </pre>
+     */
+    public static final String TYPE_ASSIGNMENT = "type="+TYPE;
+
+
+
+    /**
+     * Creates a new instance of JMXDomain. The MBeans contained in this
+     * domain are handled by the {@code virtualServer} object given to
+     * this constructor. Frequently, this will be an instance of
+     * {@link MBeanServerSupport}.
+     * @param virtualServer The virtual server that acts as a container for
+     *        the MBeans handled by this JMXDomain object. Frequently, this will
+     *        be an instance of {@link MBeanServerSupport}
+     * @see JMXNamespace#JMXNamespace(MBeanServer)
+     */
+    public JMXDomain(MBeanServer virtualServer) {
+        super(virtualServer);
+    }
+
+    /**
+     * Return the name of domain handled by this JMXDomain.
+     * @return the domain handled by this JMXDomain.
+     * @throws IOException - if the domain cannot be determined,
+     *         for instance, if the MBean is not registered yet.
+     */
+    @Override
+    public final String getDefaultDomain() {
+        final ObjectName name = getObjectName();
+        if (name == null)
+            throw new IllegalStateException("DefaultDomain is not yet known");
+        final String dom = name.getDomain();
+        return dom;
+    }
+
+    /**
+     * Returns a singleton array, containing the only domain handled by
+     * this JMXDomain object. This is
+     * {@code new String[] {getDefaultDomain()}}.
+     * @return the only domain handled by this JMXDomain.
+     * @throws IOException if the domain cannot be determined,
+     *         for instance, if the MBean is not registered yet.
+     * @see #getDefaultDomain()
+     */
+    @Override
+    public final String[] getDomains() {
+        return new String[] {getDefaultDomain()};
+    }
+
+    /**
+     * This method returns the number of MBeans in the domain handled
+     * by this JMXDomain object. The default implementation is:
+     * <pre>
+     *    getSourceServer().queryNames(
+     *        new ObjectName(getObjectName().getDomain()+":*"), null).size();
+     * </pre>
+     * If this JMXDomain is not yet registered, this method returns 0.
+     * Subclasses can override the above behavior and provide a better
+     * implementation.
+     * <p>
+     * The getMBeanCount() method is called when computing the number
+     * of MBeans in the {@linkplain #getMBeanServer() containing MBeanServer}.
+     * @return the number of MBeans in this domain, or 0.
+     */
+    @Override
+    public Integer getMBeanCount()  {
+        final ObjectName name = getObjectName();
+        if (name == null) return 0;
+        try {
+            return getSourceServer().
+               queryNames(ObjectName.WILDCARD.withDomain(name.getDomain()),
+               null).size();
+        } catch (RuntimeException x) {
+            throw x;
+        } catch (Exception x) {
+            throw new RuntimeException("Unexpected exception: "+x,x);
+        }
+    }
+
+
+
+    /**
+     * Return a canonical handler name for the provided local
+     * <var>domain</var> name, or null if the provided domain name is
+     * {@code null}.
+     * If not null, the handler name returned will be
+     * {@code domain+":type="+}{@link #TYPE TYPE}, for example
+     * {@code foo:type=JMXDomain}.
+     * @param domain A domain name
+     * @return a canonical ObjectName for a domain handler.
+     * @throws IllegalArgumentException if the provided
+     *         <var>domain</var> is not valid - e.g it contains "//".
+     */
+    public static ObjectName getDomainObjectName(String domain) {
+        if (domain == null) return null;
+        if (domain.contains(NAMESPACE_SEPARATOR))
+            throw new IllegalArgumentException(domain);
+        try {
+            return ObjectName.getInstance(domain, "type", TYPE);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(domain,x);
+        }
+    }
+
+
+    /**
+     * Validate the ObjectName supplied to preRegister.
+     * This method is introduced to allow standard subclasses to use
+     * an alternate naming scheme. For instance - if we want to
+     * reuse JMXNamespace in order to implement sessions...
+     * It is however only available for subclasses in this package.
+     **/
+    @Override
+    ObjectName validateHandlerName(ObjectName supliedName) {
+        if (supliedName == null)
+            throw new IllegalArgumentException("Must supply a valid name");
+        final String dirName = JMXNamespaces.
+                normalizeNamespaceName(supliedName.getDomain());
+        final ObjectName handlerName = getDomainObjectName(dirName);
+        if (!supliedName.equals(handlerName))
+            throw new IllegalArgumentException("invalid name space name: "+
+                        supliedName);
+
+        return supliedName;
+    }
+
+    /**
+     * This method is called by the JMX framework to register a
+     * NotificationListener that will forward {@linkplain
+     * javax.management.MBeanServerNotification mbean server notifications}
+     * through the delegate of the {@linkplain #getMBeanServer()
+     * containing MBeanServer}.
+     * The default implementation of this method is to call
+     * <pre>
+     *    getSourceServer().addNotificationListener(
+     *           MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
+     * </pre>
+     * Subclasses can redefine this behavior if needed. In particular,
+     * subclasses can send their own instances of {@link
+     * javax.management.MBeanServerNotification} by calling
+     * {@code listener.handleNotification()}.
+     *
+     * @param listener The MBeanServerNotification listener for this domain.
+     * @param filter   A notification filter.
+     */
+    public void addMBeanServerNotificationListener(
+            NotificationListener listener, NotificationFilter filter) {
+        try {
+            getSourceServer().addNotificationListener(
+                MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
+        } catch(InstanceNotFoundException x) {
+            throw new UnsupportedOperationException(
+                    "Unexpected exception: " +
+                    "Emission of MBeanServerNotification disabled.", x);
+        }
+    }
+
+    /**
+     * This method is called by the JMX framework to remove the
+     * NotificationListener that was added with {@link
+     * #addMBeanServerNotificationListener addMBeanServerNotificationListener}.
+     * The default implementation of this method is to call
+     * <pre>
+     *    getSourceServer().removeNotificationListener(
+     *           MBeanServerDelegate.DELEGATE_NAME, listener);
+     * </pre>
+     * Subclasses can redefine this behavior if needed.
+     *
+     * @param listener The MBeanServerNotification listener for this domain.
+     * @throws ListenerNotFoundException if the listener is not found.
+     */
+    public void removeMBeanServerNotificationListener(
+            NotificationListener listener)
+            throws ListenerNotFoundException {
+        try {
+            getSourceServer().removeNotificationListener(
+                MBeanServerDelegate.DELEGATE_NAME, listener);
+        } catch(InstanceNotFoundException x) {
+            throw new UnsupportedOperationException(
+                    "Unexpected exception: " +
+                    "Emission of MBeanServerNotification disabled.", x);
+        }
+    }
+
+}