/* * 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.ObjectName; /** * A special {@link JMXNamespace} that can handle part of * the MBeanServer local name space. *

* A {@code JMXDomain} makes a domain X of a * {@linkplain #getSourceServer() source MBean server} appear in the same domain * X of a containing {@code MBeanServer} in which the * {@code JMXDomain} MBean {@linkplain #getMBeanServer() is registered}. *

*

* The JMX infrastructure of the containing {@code MBeanServer} takes care of * routing all calls to MBeans whose names have domain X to the * {@linkplain #getSourceServer() source MBean server} exported by the * {@code JMXDomain} MBean in charge of domain X. *

*

* 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. *

* *

Differences between {@code JMXNamespace} and {@code JMXDomain}

* *

* 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}. *

* *

* 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. *

* *

Permission checks

* *

* 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. *

* *

First, if there is no security manager ({@link * System#getSecurityManager()} is null), that containing * {@code MBeanServer} is free not to make any checks. *

* *

* 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 X handled by a {@code JMXDomain} * in the same way that it would do for MBeans registered in its own local * repository, and as described in * the MBeanServer interface, with the following exceptions: *

* *

* For those permissions that require a {@code className}, the * className 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: *

* * *

If a security check fails, the method throws {@link * SecurityException}.

* *

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 * className.

* * 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 * MBeanServer interface. * *

See the MBeanServer interface * for more details on permission checks.

* * @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: *
     * {@code ":"}+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
     * 
*/ 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: *
     * {@code }+":"+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
     * 
*/ 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: *
     *    getSourceServer().queryNames(
     *        new ObjectName(getObjectName().getDomain()+":*"), null).size();
     * 
* If this JMXDomain is not yet registered, this method returns 0. * Subclasses can override the above behavior and provide a better * implementation. *

* 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 * domain 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 * domain 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 contains " + NAMESPACE_SEPARATOR+": "+domain); return ObjectName.valueOf(domain, "type", TYPE); } /** * 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 suppliedName) { if (suppliedName == null) throw new IllegalArgumentException("Must supply a valid name"); final String dirName = JMXNamespaces. normalizeNamespaceName(suppliedName.getDomain()); final ObjectName handlerName = getDomainObjectName(dirName); if (!suppliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ suppliedName); return suppliedName; } /** * 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 *

     *    getSourceServer().addNotificationListener(
     *           MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
     * 
* 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 *
     *    getSourceServer().removeNotificationListener(
     *           MBeanServerDelegate.DELEGATE_NAME, listener);
     * 
* 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); } } }