/* * 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. *
* 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. *
* ** 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. *
* ** 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:
*
For {@code createMBean} operations, the {@code className} of the * permission you need is the {@code className} passed as first parameter * to {@code createMBean}.
* *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}.
* *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. *
* *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. *
*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.
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); 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 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);
}
}
}