jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java
changeset 1156 bbc2d15aaf7a
parent 1002 1e6a1b77f22a
child 1222 78e3d021d528
--- a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,33 +25,49 @@
 
 package com.sun.jmx.interceptor;
 
-// java import
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.WeakHashMap;
+
+// JMX RI
+import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
+import com.sun.jmx.mbeanserver.DynamicMBean2;
+import com.sun.jmx.mbeanserver.Introspector;
+import com.sun.jmx.mbeanserver.MBeanInjector;
+import com.sun.jmx.mbeanserver.MBeanInstantiator;
+import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
+import com.sun.jmx.mbeanserver.NamedObject;
+import com.sun.jmx.mbeanserver.NotifySupport;
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.remote.util.EnvHelp;
+
 import java.lang.ref.WeakReference;
 import java.security.AccessControlContext;
+import java.security.AccessController;
 import java.security.Permission;
+import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
 
 // JMX import
 import javax.management.Attribute;
 import javax.management.AttributeList;
 import javax.management.AttributeNotFoundException;
 import javax.management.DynamicMBean;
+import javax.management.DynamicWrapperMBean;
 import javax.management.InstanceAlreadyExistsException;
 import javax.management.InstanceNotFoundException;
 import javax.management.IntrospectionException;
 import javax.management.InvalidAttributeValueException;
 import javax.management.JMRuntimeException;
 import javax.management.ListenerNotFoundException;
-import javax.management.MalformedObjectNameException;
 import javax.management.MBeanException;
 import javax.management.MBeanInfo;
 import javax.management.MBeanPermission;
@@ -64,6 +80,7 @@
 import javax.management.NotCompliantMBeanException;
 import javax.management.Notification;
 import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
 import javax.management.NotificationEmitter;
 import javax.management.NotificationFilter;
 import javax.management.NotificationListener;
@@ -75,22 +92,7 @@
 import javax.management.RuntimeErrorException;
 import javax.management.RuntimeMBeanException;
 import javax.management.RuntimeOperationsException;
-
-// JMX RI
-import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
-import com.sun.jmx.mbeanserver.DynamicMBean2;
-import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
-import com.sun.jmx.mbeanserver.MBeanInstantiator;
-import com.sun.jmx.mbeanserver.Repository;
-import com.sun.jmx.mbeanserver.NamedObject;
-import com.sun.jmx.mbeanserver.Introspector;
-import com.sun.jmx.mbeanserver.MBeanInjector;
-import com.sun.jmx.mbeanserver.NotifySupport;
-import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
-import com.sun.jmx.mbeanserver.Util;
-import com.sun.jmx.remote.util.EnvHelp;
-import javax.management.DynamicWrapperMBean;
-import javax.management.NotificationBroadcasterSupport;
+import javax.management.namespace.JMXNamespace;
 
 /**
  * This is the default class for MBean manipulation on the agent side. It
@@ -113,7 +115,8 @@
  *
  * @since 1.5
  */
-public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
+public class DefaultMBeanServerInterceptor
+        extends MBeanServerInterceptorSupport {
 
     /** The MBeanInstantiator object used by the
      *  DefaultMBeanServerInterceptor */
@@ -123,7 +126,7 @@
      *  DefaultMBeanServerInterceptor */
     private transient MBeanServer server = null;
 
-    /** The MBean server object taht associated to the
+    /** The MBean server delegate object that is associated to the
      *  DefaultMBeanServerInterceptor */
     private final transient MBeanServerDelegate delegate;
 
@@ -138,13 +141,15 @@
                 new WeakHashMap<ListenerWrapper,
                                 WeakReference<ListenerWrapper>>();
 
+    private final NamespaceDispatchInterceptor dispatcher;
+
     /** The default domain of the object names */
     private final String domain;
 
-    /** True if the repository perform queries, false otherwise */
-    private boolean queryByRepo;
+    /** The mbeanServerName  */
+    private final String mbeanServerName;
 
-    /** The sequence number identifyng the notifications sent */
+    /** The sequence number identifying the notifications sent */
     // Now sequence number is handled by MBeanServerDelegate.
     // private int sequenceNumber=0;
 
@@ -162,11 +167,13 @@
      * @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.
+     * @param dispatcher The dispatcher used by this MBeanServer
      */
     public DefaultMBeanServerInterceptor(MBeanServer         outer,
                                          MBeanServerDelegate delegate,
                                          MBeanInstantiator   instantiator,
-                                         Repository          repository)  {
+                                         Repository          repository,
+                                         NamespaceDispatchInterceptor dispatcher)  {
         if (outer == null) throw new
             IllegalArgumentException("outer MBeanServer cannot be null");
         if (delegate == null) throw new
@@ -181,6 +188,8 @@
         this.instantiator = instantiator;
         this.repository   = repository;
         this.domain       = repository.getDefaultDomain();
+        this.dispatcher   = dispatcher;
+        this.mbeanServerName = Util.getMBeanServerSecurityName(delegate);
     }
 
     public ObjectInstance createMBean(String className, ObjectName name)
@@ -259,8 +268,8 @@
             name = nonDefaultDomain(name);
         }
 
-        checkMBeanPermission(className, null, null, "instantiate");
-        checkMBeanPermission(className, null, name, "registerMBean");
+        checkMBeanPermission(mbeanServerName,className, null, null, "instantiate");
+        checkMBeanPermission(mbeanServerName,className, null, name, "registerMBean");
 
         /* Load the appropriate class. */
         if (withDefaultLoaderRepository) {
@@ -324,7 +333,7 @@
 
         final String infoClassName = getNewMBeanClassName(object);
 
-        checkMBeanPermission(infoClassName, null, name, "registerMBean");
+        checkMBeanPermission(mbeanServerName,infoClassName, null, name, "registerMBean");
         checkMBeanTrustPermission(theClass);
 
         return registerObject(infoClassName, object, name);
@@ -433,7 +442,8 @@
         DynamicMBean instance = getMBean(name);
         // may throw InstanceNotFoundException
 
-        checkMBeanPermission(instance, null, name, "unregisterMBean");
+        checkMBeanPermission(mbeanServerName, instance, null, name,
+                "unregisterMBean");
 
         if (instance instanceof MBeanRegistration)
             preDeregisterInvoke((MBeanRegistration) instance);
@@ -467,7 +477,8 @@
         name = nonDefaultDomain(name);
         DynamicMBean instance = getMBean(name);
 
-        checkMBeanPermission(instance, null, name, "getObjectInstance");
+        checkMBeanPermission(mbeanServerName,
+                instance, null, name, "getObjectInstance");
 
         final String className = getClassName(instance);
 
@@ -479,7 +490,7 @@
         if (sm != null) {
             // Check if the caller has the right to invoke 'queryMBeans'
             //
-            checkMBeanPermission((String) null, null, null, "queryMBeans");
+            checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryMBeans");
 
             // Perform query without "query".
             //
@@ -492,7 +503,7 @@
                 new HashSet<ObjectInstance>(list.size());
             for (ObjectInstance oi : list) {
                 try {
-                    checkMBeanPermission(oi.getClassName(), null,
+                    checkMBeanPermission(mbeanServerName,oi.getClassName(), null,
                                          oi.getObjectName(), "queryMBeans");
                     allowedList.add(oi);
                 } catch (SecurityException e) {
@@ -516,11 +527,6 @@
         //
         Set<NamedObject> list = repository.query(name, query);
 
-        if (queryByRepo) {
-            // The repository performs the filtering
-            query = null;
-        }
-
         return (objectInstancesFromFilteredNamedObjects(list, query));
     }
 
@@ -530,7 +536,7 @@
         if (sm != null) {
             // Check if the caller has the right to invoke 'queryNames'
             //
-            checkMBeanPermission((String) null, null, null, "queryNames");
+            checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryNames");
 
             // Perform query without "query".
             //
@@ -543,7 +549,7 @@
                 new HashSet<ObjectInstance>(list.size());
             for (ObjectInstance oi : list) {
                 try {
-                    checkMBeanPermission(oi.getClassName(), null,
+                    checkMBeanPermission(mbeanServerName, oi.getClassName(), null,
                                          oi.getObjectName(), "queryNames");
                     allowedList.add(oi);
                 } catch (SecurityException e) {
@@ -572,11 +578,6 @@
         //
         Set<NamedObject> list = repository.query(name, query);
 
-        if (queryByRepo) {
-            // The repository performs the filtering
-            query = null;
-        }
-
         return (objectNamesFromFilteredNamedObjects(list, query));
     }
 
@@ -589,8 +590,8 @@
 
         name = nonDefaultDomain(name);
 
-//      /* Permission check */
-//      checkMBeanPermission(null, null, name, "isRegistered");
+        /* No Permission check */
+        // isRegistered is always unchecked as per JMX spec.
 
         return (repository.contains(name));
     }
@@ -600,7 +601,7 @@
         if (sm != null) {
             // Check if the caller has the right to invoke 'getDomains'
             //
-            checkMBeanPermission((String) null, null, null, "getDomains");
+            checkMBeanPermission(mbeanServerName, (String) null, null, null, "getDomains");
 
             // Return domains
             //
@@ -612,8 +613,9 @@
             List<String> result = new ArrayList<String>(domains.length);
             for (int i = 0; i < domains.length; i++) {
                 try {
-                    ObjectName domain = Util.newObjectName(domains[i] + ":x=x");
-                    checkMBeanPermission((String) null, null, domain, "getDomains");
+                    ObjectName dom =
+                            Util.newObjectName(domains[i] + ":x=x");
+                    checkMBeanPermission(mbeanServerName, (String) null, null, dom, "getDomains");
                     result.add(domains[i]);
                 } catch (SecurityException e) {
                     // OK: Do not add this domain to the list
@@ -657,7 +659,8 @@
         }
 
         final DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, attribute, name, "getAttribute");
+        checkMBeanPermission(mbeanServerName, instance, attribute,
+                name, "getAttribute");
 
         try {
             return instance.getAttribute(attribute);
@@ -702,7 +705,7 @@
 
             // Check if the caller has the right to invoke 'getAttribute'
             //
-            checkMBeanPermission(classname, null, name, "getAttribute");
+            checkMBeanPermission(mbeanServerName, classname, null, name, "getAttribute");
 
             // Check if the caller has the right to invoke 'getAttribute'
             // on each specific attribute
@@ -711,14 +714,15 @@
                 new ArrayList<String>(attributes.length);
             for (String attr : attributes) {
                 try {
-                    checkMBeanPermission(classname, attr,
+                    checkMBeanPermission(mbeanServerName, classname, attr,
                                          name, "getAttribute");
                     allowedList.add(attr);
                 } catch (SecurityException e) {
                     // OK: Do not add this attribute to the list
                 }
             }
-            allowedAttributes = allowedList.toArray(new String[0]);
+            allowedAttributes =
+                    allowedList.toArray(new String[allowedList.size()]);
         }
 
         try {
@@ -756,7 +760,7 @@
         }
 
         DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, attribute.getName(),
+        checkMBeanPermission(mbeanServerName, instance, attribute.getName(),
                              name, "setAttribute");
 
         try {
@@ -799,7 +803,7 @@
 
             // Check if the caller has the right to invoke 'setAttribute'
             //
-            checkMBeanPermission(classname, null, name, "setAttribute");
+            checkMBeanPermission(mbeanServerName, classname, null, name, "setAttribute");
 
             // Check if the caller has the right to invoke 'setAttribute'
             // on each specific attribute
@@ -808,7 +812,7 @@
             for (Iterator i = attributes.iterator(); i.hasNext();) {
                 try {
                     Attribute attribute = (Attribute) i.next();
-                    checkMBeanPermission(classname, attribute.getName(),
+                    checkMBeanPermission(mbeanServerName, classname, attribute.getName(),
                                          name, "setAttribute");
                     allowedAttributes.add(attribute);
                 } catch (SecurityException e) {
@@ -832,7 +836,8 @@
         name = nonDefaultDomain(name);
 
         DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, operationName, name, "invoke");
+        checkMBeanPermission(mbeanServerName, instance, operationName,
+                name, "invoke");
         try {
             return instance.invoke(operationName, params, signature);
         } catch (Throwable t) {
@@ -934,8 +939,7 @@
                     "registerMBean", "ObjectName = " + name);
         }
 
-        ObjectName logicalName = name;
-        logicalName = preRegister(mbean, server, name);
+        ObjectName logicalName = preRegister(mbean, server, name);
 
         // preRegister returned successfully, so from this point on we
         // must call postRegister(false) if there is any problem.
@@ -961,16 +965,17 @@
 
             if (logicalName != name && logicalName != null) {
                 logicalName =
-                    ObjectName.getInstance(nonDefaultDomain(logicalName));
+                        ObjectName.getInstance(nonDefaultDomain(logicalName));
             }
 
-            checkMBeanPermission(classname, null, logicalName, "registerMBean");
+            checkMBeanPermission(mbeanServerName, classname, null, logicalName,
+                    "registerMBean");
 
             if (logicalName == null) {
                 final RuntimeException wrapped =
-                        new IllegalArgumentException("No object name specified");
+                    new IllegalArgumentException("No object name specified");
                 throw new RuntimeOperationsException(wrapped,
-                                                     "Exception occurred trying to register the MBean");
+                            "Exception occurred trying to register the MBean");
             }
 
             final Object resource = getResource(mbean);
@@ -987,13 +992,15 @@
             //
             context = registerWithRepository(resource, mbean, logicalName);
 
+
             registerFailed = false;
             registered = true;
+
         } finally {
             try {
                 postRegister(logicalName, mbean, registered, registerFailed);
             } finally {
-                if (registered) context.done();
+                if (registered && context!=null) context.done();
             }
         }
         return new ObjectInstance(logicalName, classname);
@@ -1001,20 +1008,19 @@
 
     private static void throwMBeanRegistrationException(Throwable t, String where)
     throws MBeanRegistrationException {
-        try {
-            throw t;
-        } catch (RuntimeException e) {
-                throw new RuntimeMBeanException(
-                        e, "RuntimeException thrown " + where);
-        } catch (Error er) {
-                throw new RuntimeErrorException(er, "Error thrown " + where);
-        } catch (MBeanRegistrationException r) {
-            throw r;
-        } catch (Exception ex) {
-            throw new MBeanRegistrationException(ex, "Exception thrown " + where);
-        } catch (Throwable t1) {
-            throw new RuntimeException(t);  // neither Error nor Exception??
-        }
+        if (t instanceof RuntimeException) {
+            throw new RuntimeMBeanException((RuntimeException)t,
+                    "RuntimeException thrown " + where);
+        } else if (t instanceof Error) {
+            throw new RuntimeErrorException((Error)t,
+                    "Error thrown " + where);
+        } else if (t instanceof MBeanRegistrationException) {
+            throw (MBeanRegistrationException)t;
+        } else if (t instanceof Exception) {
+            throw new MBeanRegistrationException((Exception)t,
+                    "Exception thrown " + where);
+        } else // neither Error nor Exception??
+            throw new RuntimeException(t);
     }
 
     private static ObjectName preRegister(
@@ -1230,7 +1236,8 @@
         }
 
         DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, null, name, "addNotificationListener");
+        checkMBeanPermission(mbeanServerName, instance, null,
+                name, "addNotificationListener");
 
         NotificationBroadcaster broadcaster =
                 getNotificationBroadcaster(name, instance,
@@ -1367,7 +1374,7 @@
         }
 
         DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, null, name,
+        checkMBeanPermission(mbeanServerName, instance, null, name,
                              "removeNotificationListener");
 
         /* We could simplify the code by assigning broadcaster after
@@ -1438,7 +1445,7 @@
             throw new JMRuntimeException("MBean " + name +
                                          "has no MBeanInfo");
 
-        checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo");
+        checkMBeanPermission(mbeanServerName, mbi.getClassName(), null, name, "getMBeanInfo");
 
         return mbi;
     }
@@ -1446,8 +1453,9 @@
     public boolean isInstanceOf(ObjectName name, String className)
         throws InstanceNotFoundException {
 
-        DynamicMBean instance = getMBean(name);
-        checkMBeanPermission(instance, null, name, "isInstanceOf");
+        final DynamicMBean instance = getMBean(name);
+        checkMBeanPermission(mbeanServerName,
+                instance, null, name, "isInstanceOf");
 
         try {
             Object resource = getResource(instance);
@@ -1498,7 +1506,8 @@
         throws InstanceNotFoundException {
 
         DynamicMBean instance = getMBean(mbeanName);
-        checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
+        checkMBeanPermission(mbeanServerName, instance, null, mbeanName,
+                "getClassLoaderFor");
         return getResourceLoader(instance);
     }
 
@@ -1513,12 +1522,13 @@
             throws InstanceNotFoundException {
 
         if (loaderName == null) {
-            checkMBeanPermission((String) null, null, null, "getClassLoader");
+            checkMBeanPermission(mbeanServerName, (String) null, null, null, "getClassLoader");
             return server.getClass().getClassLoader();
         }
 
         DynamicMBean instance = getMBean(loaderName);
-        checkMBeanPermission(instance, null, loaderName, "getClassLoader");
+        checkMBeanPermission(mbeanServerName, instance, null, loaderName,
+                "getClassLoader");
 
         Object resource = getResource(instance);
 
@@ -1568,7 +1578,7 @@
             }
         } else {
             // Access the filter
-            MBeanServer oldServer = QueryEval.getMBeanServer();
+            final MBeanServer oldServer = QueryEval.getMBeanServer();
             query.setMBeanServer(server);
             try {
                 for (NamedObject no : list) {
@@ -1817,26 +1827,30 @@
             return mbean.getMBeanInfo().getClassName();
     }
 
-    private static void checkMBeanPermission(DynamicMBean mbean,
+    private static void checkMBeanPermission(String mbeanServerName,
+                                             DynamicMBean mbean,
                                              String member,
                                              ObjectName objectName,
                                              String actions) {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            checkMBeanPermission(safeGetClassName(mbean),
+            checkMBeanPermission(mbeanServerName,
+                                 safeGetClassName(mbean),
                                  member,
                                  objectName,
                                  actions);
         }
     }
 
-    private static void checkMBeanPermission(String classname,
+    private static void checkMBeanPermission(String mbeanServerName,
+                                             String classname,
                                              String member,
                                              ObjectName objectName,
                                              String actions) {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            Permission perm = new MBeanPermission(classname,
+            Permission perm = new MBeanPermission(mbeanServerName,
+                                                  classname,
                                                   member,
                                                   objectName,
                                                   actions);
@@ -1902,6 +1916,12 @@
             throws InstanceAlreadyExistsException,
             MBeanRegistrationException {
 
+        // this will throw an exception if the pair (resource, logicalName)
+        // violates namespace conventions - for instance, if logicalName
+        // ends with // but resource is not a JMXNamespace.
+        //
+        checkResourceObjectNameConstraints(resource, logicalName);
+
         // Creates a registration context, if needed.
         //
         final ResourceContext context =
@@ -1967,6 +1987,57 @@
         return context;
     }
 
+
+    /**
+     * Checks that the ObjectName is legal with regards to the
+     * type of the MBean resource.
+     * If the MBean name is  domain:type=JMXDomain, the
+     *     MBean must be a JMXDomain.
+     * If the MBean name is  namespace//:type=JMXNamespace, the
+     *     MBean must be a JMXNamespace.
+     * If the MBean is a JMXDomain, its name
+     *      must be domain:type=JMXDomain.
+     * If the MBean is a JMXNamespace,  its name
+     *      must be namespace//:type=JMXNamespace.
+     */
+    private void checkResourceObjectNameConstraints(Object resource,
+            ObjectName logicalName)
+            throws MBeanRegistrationException {
+        try {
+            dispatcher.checkLocallyRegistrable(resource, logicalName);
+        } catch (Throwable x) {
+            DefaultMBeanServerInterceptor.throwMBeanRegistrationException(x, "validating ObjectName");
+        }
+    }
+
+    /**
+     * Registers a JMXNamespace with the dispatcher.
+     * This method is called by the ResourceContext from within the
+     * repository lock.
+     * @param namespace    The JMXNamespace
+     * @param logicalName  The JMXNamespaceMBean ObjectName
+     * @param postQueue    A queue that will be processed after postRegister.
+     */
+    private void addJMXNamespace(JMXNamespace namespace,
+            final ObjectName logicalName,
+            final Queue<Runnable> postQueue) {
+        dispatcher.addNamespace(logicalName, namespace, postQueue);
+    }
+
+    /**
+     * Unregisters a JMXNamespace from the dispatcher.
+     * This method is called by the ResourceContext from within the
+     * repository lock.
+     * @param namespace    The JMXNamespace
+     * @param logicalName  The JMXNamespaceMBean ObjectName
+     * @param postQueue    A queue that will be processed after postDeregister.
+     */
+    private void removeJMXNamespace(JMXNamespace namespace,
+            final ObjectName logicalName,
+            final Queue<Runnable> postQueue) {
+        dispatcher.removeNamespace(logicalName, namespace, postQueue);
+    }
+
     /**
      * Registers a ClassLoader with the CLR.
      * This method is called by the ResourceContext from within the
@@ -2020,6 +2091,52 @@
         }
     }
 
+
+    /**
+     * Creates a ResourceContext for a JMXNamespace MBean.
+     * The resource context makes it possible to add the JMXNamespace to
+     * (ResourceContext.registering) or resp. remove the JMXNamespace from
+     * (ResourceContext.unregistered) the NamespaceDispatchInterceptor
+     * when the associated MBean is added to or resp. removed from the
+     * repository.
+     * Note: JMXDomains are special sub classes of JMXNamespaces and
+     *       are also handled by this object.
+     *
+     * @param namespace    The JMXNamespace MBean being registered or
+     *                     unregistered.
+     * @param logicalName  The name of the JMXNamespace MBean.
+     * @return a ResourceContext that takes in charge the addition or removal
+     *         of the namespace to or from the NamespaceDispatchInterceptor.
+     */
+    private ResourceContext createJMXNamespaceContext(
+            final JMXNamespace namespace,
+            final ObjectName logicalName) {
+        final Queue<Runnable> doneTaskQueue = new LinkedList<Runnable>();
+        return new ResourceContext() {
+
+            public void registering() {
+                addJMXNamespace(namespace, logicalName, doneTaskQueue);
+            }
+
+            public void unregistered() {
+                removeJMXNamespace(namespace, logicalName,
+                                   doneTaskQueue);
+            }
+
+            public void done() {
+                for (Runnable r : doneTaskQueue) {
+                    try {
+                        r.run();
+                    } catch (RuntimeException x) {
+                        MBEANSERVER_LOGGER.log(Level.FINE,
+                                "Failed to process post queue for "+
+                                logicalName, x);
+                    }
+                }
+            }
+        };
+    }
+
     /**
      * Creates a ResourceContext for a ClassLoader MBean.
      * The resource context makes it possible to add the ClassLoader to
@@ -2065,10 +2182,16 @@
      */
     private ResourceContext makeResourceContextFor(Object resource,
             ObjectName logicalName) {
+        if (resource instanceof JMXNamespace) {
+            return createJMXNamespaceContext((JMXNamespace) resource,
+                    logicalName);
+        }
         if (resource instanceof ClassLoader) {
             return createClassLoaderContext((ClassLoader) resource,
                     logicalName);
         }
         return ResourceContext.NONE;
     }
+
+
 }