/* * 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 java.util.UUID; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.ObjectName; /** * MBean Servers can be federated into a single hierarchical name space: * A JMXNamespace is an MBean that handles a sub name space in that * hierarchical name space. *

* A name space is created simply by registering a {@code JMXNamespace} * MBean in the MBean Server. The name of the created name space is defined * by the {@linkplain JMXNamespaces#getNamespaceObjectName name of the JMXNamespace} * that handles it. A name space is equivalent to * an MBean Server within an MBean Server. When creating a {@code JMXNamespace}, * the MBean Server within is passed to the constructor. *

*

* The {@code JMXNamespace} class is the base class for implementing * all name space handlers. All name space handlers must be instances of * {@code JMXNamespace} or a subclass of it. *

*

* A concrete example of a {@code JMXNamespace} MBean subclass * is the {@link JMXRemoteNamespace JMXRemoteNamespace} MBean which * is able to mirror all MBeans contained in a remote MBean server known by its * {@link javax.management.remote.JMXServiceURL}. *

*

* You can create a local namespace by supplying a newly created MBean Server * to an instance of {@code JMXNamespace}. For instance: *

 * final String namespace = "foo";
 * final ObjectName namespaceName = {@link JMXNamespaces#getNamespaceObjectName
 *       JMXNamespaces.getNamespaceObjectName(namespace)};
 * server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
 *                      namespaceName);
 * 
*

*

* Note: A JMXNamespace MBean cannot be registered * simultaneously in two different * MBean servers, or indeed in the same MBean Server with two * different names. It is however possible to give the same MBeanServer * instance to two different JMXNamespace MBeans, and thus create a graph * rather than a tree. *

* *

To view the content of a namespace, you will usually use an * instance of {@link JMXNamespaceView}. For instance, given the * namespace {@code "foo"} created above, you would do: *

*
 * final JMXNamespaceView view = new JMXNamespaceView(server);
 * System.out.println("List of namespaces: "+Arrays.toString({@link JMXNamespaceView#list() view.list()}));
 *
 * final JMXNamespaceView foo  = {@link JMXNamespaceView#down view.down("foo")};
 * System.out.println({@link JMXNamespaceView#where() foo.where()}+" contains: " +
 *        {@link JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
 * 
* *

JMX Namespace Permission Checks

* *

A special {@link JMXNamespacePermission} is defined to check access * to MBean within namespaces.

*

When a JMXNamespace MBean is registered in an * MBean server created through the default {@link * javax.management.MBeanServerBuilder}, and if a {@link * SecurityManager SecurityManager} is * {@linkplain System#getSecurityManager() present}, the MBeanServer will * check a {@link JMXNamespacePermission} before invoking * any method on the {@linkplain #getSourceServer source MBeanServer} of the * JMXNamespace. * {@linkplain JMXNamespacePermission JMX Namespace Permissions} are similar to * {@linkplain javax.management.MBeanPermission MBean Permissions}, except * that you usually cannot specify an MBean class name. You can however * specify object name patterns - which will allow you for example to only grant * permissions for MBeans having a specific {@code type=} key * in their object name. *

* Another difference is that {@link JMXNamespacePermission * JMXNamespacePermission} also specifies from which namespace and which * MBean server the permission is granted. *

*

In the rest of this document, the following terms are used:

* * *

For instance let's assume that some piece of code calls:

*
 *     final MBeanServer mbeanServer = ...;
 *     final ObjectName  name   = new ObjectName("a//b//c//D:k=v");
 *     mbeanServer.getAttribute(name,"Foo");
 * 
*

* Assuming that there is a security manager, or that the * implementation chooses to make checks anyway, the checks that will * be made in that case are: *

*
    *
  1. * JMXNamespacePermission(mbeanServerName, "Foo", "a//b//c//D:k=v", * "getAttribute") * (where {@code mbeanServerName=MBeanServerFactory.getMBeanServerName(mbeanServer)}, * namespace="a", and {@code mbean="b//c//D:k=v"}) *
  2. *
  3. and in addition if namespace {@code "a"} is local, * JMXNamespacePermission(aSourceServerName,"Foo","b//c//D:k=v", * "getAttribute")} * (where * {@code aSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(a))}, * namespace="b", and {@code mbean="c//D:k=v"}), *
  4. *
  5. and in addition if namespace {@code "b"} is also local, * JMXNamespacePermission(bSourceServerName,"Foo","c//D:k=v", * "getAttribute")} * (where * {@code bSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(b))}, * namespace="c", and {@code mbean="D:k=v"}), *
  6. *
  7. and in addition if the source mbean server of namespace * {@code "c"} is a also a local MBeanServer in this JVM, * {@code MBeanPermission(cSourceServerName,,"Foo","D:k=v","getAttrinute")}, * (where * {@code cSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(c))}). *
  8. *
*

For any of these MBean servers, if no name was supplied when * creating that MBeanServer the {@link JMXNamespacePermission} is * created with an {@code mbeanServerName} equal to * {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}. *

*

If the namespace {@code a} is in fact a remote {@code MBeanServer}, * for instance because namespace {@code a} is implemented by a {@link * JMXRemoteNamespace} pointing to a distant MBeanServer located in * another JMX agent, then checks 2, * 3, and 4 will not * be performed in the local JVM. They might or might not be performed in * the remote agent, depending on how access control and permission * checking are configured in the remote agent, and how authentication * is configured in the connector used by the {@link * JMXRemoteNamespace}. *

*

In all cases, {@linkplain JMXNamespacePermission JMX Namespace Permissions} * are checked as follows:

*

First, if there is no security manager ({@link * System#getSecurityManager()} is null), then an implementation of * of MBeanServer that supports JMX namespaces is free not to make any * checks.

* *

Assuming that there is a security manager, or that the * implementation chooses to make checks anyway, the checks are made * as detailed below.

* *

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

* * *

*

It must be noted that if all namespaces are local, and all * local namespaces are implemented by regular MBean servers, that is, there * are no {@linkplain MBeanServerSupport Virtual Namespaces}, then * simple {@linkplain javax.management.MBeanPermission MBean Permission} * checks might be enough to secure an application. * In that case, it is possible to specify the following {@link * JMXNamespacePermission} permission in the policy file, which implies all * other JMX namespace permissions: *

*
 *     permission javax.management.namespace.JMXNamespacePermission "*::*[]", "*";
 * 
* * @since 1.7 */ public class JMXNamespace implements JMXNamespaceMBean, MBeanRegistration { /** * The standard value of the {@code type} * property key that must be used to construct valid {@link * JMXNamespaceMBean} ObjectNames.
* This is {@value #TYPE}. **/ public static final String TYPE = "JMXNamespace"; /** * The {@link ObjectName#getKeyPropertyListString keyPropertyListString} * that must be used to construct valid {@link JMXNamespaceMBean} * ObjectNames.
* This is * {@value #TYPE_ASSIGNMENT}. **/ public static final String TYPE_ASSIGNMENT = "type="+TYPE; private volatile MBeanServer mbeanServer; // the mbean server in which // this MBean is registered. private volatile ObjectName objectName; // the ObjectName of this MBean. private final MBeanServer sourceServer; // the MBeanServer within = the // name space (or the MBean server // that contains it). private final String uuid; /** * Creates a new JMXNamespace implemented by means of an MBean Server. * A namespace is equivalent to an MBeanServer within an MBean Server. * The {@code sourceServer} provided to this constructor is the MBean Server * within. * @param sourceServer the MBean server that implemented by this namespace. * @see #getSourceServer */ public JMXNamespace(MBeanServer sourceServer) { this.sourceServer = sourceServer; this.uuid = UUID.randomUUID().toString(); } /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} * interface in order to get a handle to the MBean server in which it is * registered. It also check the validity of its own ObjectName. *

* This method is called by the MBean server. * Application classes should never call this method directly. *

* If this method is overridden, the overriding method should call * {@code super.preRegister(server,name)}. * @see MBeanRegistration#preRegister MBeanRegistration * @see JMXNamespaces#getNamespaceObjectName getNamespaceObjectName * @param name The object name of the MBean. name must be a * syntactically valid JMXNamespace name, as returned by * {@link JMXNamespaces#getNamespaceObjectName(java.lang.String) * getNamespaceObjectName(namespace)}. * @return The name under which the MBean is to be registered. * @throws IllegalArgumentException if the name supplied is not valid. * @throws Exception can be thrown by subclasses. */ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { if (objectName != null && ! objectName.equals(name)) throw new IllegalStateException( "Already registered under another name: " + objectName); objectName = validateHandlerName(name); mbeanServer = server; return name; } /** * 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. **/ 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 = JMXNamespaces.getNamespaceObjectName(dirName); if (!supliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ supliedName); return supliedName; } /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} * interface in order to get a handle to the MBean server in which it is * registered. *

* This method is called by the MBean server. Application classes should * not call this method directly. Subclasses are free to override this * method with their own specific behavior - but the overriding method * shoud still call {@code super.postRegister(registrationDone)}. * @see MBeanRegistration#postRegister MBeanRegistration */ public void postRegister(Boolean registrationDone) { // nothing to do } /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} * interface in order to get a handle to the MBean server in which it is * registered. *

* This method is called by the MBean server. Application classes should * not call this method directly. Subclasses are free to override this * method with their own specific behavior - but the overriding method * shoud still call {@code super.preDeregister()}. * @see MBeanRegistration#preDeregister MBeanRegistration */ public void preDeregister() throws Exception { // nothing to do } /** * This method is part of the {@link MBeanRegistration} interface. * It allows the {@code JMXNamespace} MBean to perform any operations * needed after having been unregistered in the MBean server. *

* This method is called by the MBean server. Application classes should * not call this method directly. If a subclass overrides this * method, the overriding method shoud call {@code super.postDeregister()}. * @see MBeanRegistration#postDeregister MBeanRegistration */ public void postDeregister() { mbeanServer = null; objectName = null; } /** * Returns the MBeanServer in which this MBean is registered, * or null. Chiefly of interest for subclasses. * @return the MBeanServer supplied to {@link #preRegister}. **/ public final MBeanServer getMBeanServer() { return mbeanServer; } /** * Returns the MBeanServer that contains or emulates the source * namespace. When a JMXNamespace MBean is registered in an * MBean server created through the default {@link * javax.management.MBeanServerBuilder}, the MBeanServer will * check {@link JMXNamespacePermission} before invoking * any method on the source MBeanServer of the JMXNamespace. * See JMX Namespace Permission Checks * above. * @return an MBeanServer view of the source namespace **/ public MBeanServer getSourceServer() { return sourceServer; } /** * Returns the ObjectName with which this MBean was registered, * or null. Chiefly of interest for subclasses. * @return the ObjectName supplied to {@link #preRegister}. **/ public final ObjectName getObjectName() { return objectName; } /** * HandlerName used in traces. **/ String getHandlerName() { final ObjectName name = getObjectName(); if (name != null) return name.toString(); return this.toString(); } /** * In this class, this method returns {@link #getSourceServer * getSourceServer()}.{@link javax.management.MBeanServer#getMBeanCount * getMBeanCount()}. *
This default behaviour may be redefined in subclasses. * @throws java.io.IOException can be thrown by subclasses. */ public Integer getMBeanCount() throws IOException { return getSourceServer().getMBeanCount(); } /** * In this class, this method returns {@link #getSourceServer * getSourceServer()}.{@link javax.management.MBeanServer#getDomains * getDomains()}. *
This default behaviour may be redefined in subclasses. * @throws java.io.IOException can be thrown by subclasses. */ public String[] getDomains() throws IOException { return getSourceServer().getDomains(); } /** * In this class, this method returns {@link #getSourceServer * getSourceServer()}.{@link javax.management.MBeanServer#getDefaultDomain * getDefaultDomain()}. *
This default behaviour may be redefined in subclasses. * @throws java.io.IOException can be thrown by subclasses. */ public String getDefaultDomain() throws IOException { return getSourceServer().getDefaultDomain(); } public final String getUUID() { return uuid; } }