changeset 528:9145ff046bb4

5072476: RFE: support cascaded (federated) MBean Servers 6299231: Add support for named MBean Servers Summary: New javax.management.namespace package. Reviewed-by: emcmanus
author dfuchs
date Thu, 04 Sep 2008 14:46:36 +0200
parents 00ea8fc81867
children 71a5f3f55b9c d276b0716d13
files make/docs/CORE_PKGS.gmk src/share/classes/com/sun/jmx/defaults/JmxProperties.java src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java src/share/classes/com/sun/jmx/mbeanserver/Repository.java src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java src/share/classes/com/sun/jmx/mbeanserver/Util.java src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java src/share/classes/com/sun/jmx/namespace/RoutingProxy.java src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java src/share/classes/com/sun/jmx/namespace/package.html src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java src/share/classes/com/sun/jmx/namespace/serial/package.html src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java src/share/classes/javax/management/InstanceNotFoundException.java src/share/classes/javax/management/MBeanPermission.java src/share/classes/javax/management/MBeanServer.java src/share/classes/javax/management/MBeanServerDelegate.java src/share/classes/javax/management/MBeanServerFactory.java src/share/classes/javax/management/ObjectName.java src/share/classes/javax/management/event/EventClient.java src/share/classes/javax/management/event/EventClientDelegate.java src/share/classes/javax/management/namespace/JMXDomain.java src/share/classes/javax/management/namespace/JMXNamespace.java src/share/classes/javax/management/namespace/JMXNamespaceMBean.java src/share/classes/javax/management/namespace/JMXNamespacePermission.java src/share/classes/javax/management/namespace/JMXNamespaceView.java src/share/classes/javax/management/namespace/JMXNamespaces.java src/share/classes/javax/management/namespace/JMXRemoteNamespace.java src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java src/share/classes/javax/management/namespace/MBeanServerSupport.java src/share/classes/javax/management/namespace/VirtualEventManager.java src/share/classes/javax/management/namespace/package-info.java src/share/classes/javax/management/remote/JMXConnectorFactory.java src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java test/javax/management/ObjectName/ApplyWildcardTest.java test/javax/management/namespace/DomainCreationTest.java test/javax/management/namespace/EventWithNamespaceControlTest.java test/javax/management/namespace/EventWithNamespaceTest.java test/javax/management/namespace/ExportNamespaceTest.java test/javax/management/namespace/JMXDomainTest.java test/javax/management/namespace/JMXNamespaceSecurityTest.java test/javax/management/namespace/JMXNamespaceTest.java test/javax/management/namespace/JMXNamespaceViewTest.java test/javax/management/namespace/JMXNamespacesTest.java test/javax/management/namespace/JMXRemoteNamespaceTest.java test/javax/management/namespace/JMXRemoteTargetNamespace.java test/javax/management/namespace/LazyDomainTest.java test/javax/management/namespace/MXBeanRefTest.java test/javax/management/namespace/NamespaceController.java test/javax/management/namespace/NamespaceControllerMBean.java test/javax/management/namespace/NamespaceCreationTest.java test/javax/management/namespace/NamespaceNotificationsTest.java test/javax/management/namespace/NullDomainObjectNameTest.java test/javax/management/namespace/NullObjectNameTest.java test/javax/management/namespace/QueryNamesTest.java test/javax/management/namespace/RemoveNotificationListenerTest.java test/javax/management/namespace/RoutingServerProxyTest.java test/javax/management/namespace/SerialParamProcessorTest.java test/javax/management/namespace/SourceNamespaceTest.java test/javax/management/namespace/VirtualMBeanNotifTest.java test/javax/management/namespace/VirtualMBeanTest.java test/javax/management/namespace/VirtualNamespaceQueryTest.java test/javax/management/namespace/VirtualPropsTest.java test/javax/management/namespace/Wombat.java test/javax/management/namespace/WombatMBean.java test/javax/management/namespace/namespace.policy
diffstat 89 files changed, 25862 insertions(+), 2438 deletions(-) [+]
line wrap: on
line diff
--- a/make/docs/CORE_PKGS.gmk	Wed Sep 03 14:31:17 2008 +0200
+++ b/make/docs/CORE_PKGS.gmk	Thu Sep 04 14:46:36 2008 +0200
@@ -158,6 +158,7 @@
   javax.management.event                         \
   javax.management.loading                       \
   javax.management.monitor                       \
+  javax.management.namespace                     \
   javax.management.relation                      \
   javax.management.openmbean                     \
   javax.management.timer                         \
--- a/src/share/classes/com/sun/jmx/defaults/JmxProperties.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/defaults/JmxProperties.java	Thu Sep 04 14:46:36 2008 +0200
@@ -177,6 +177,18 @@
             "javax.management.relation";
 
     /**
+     * Logger name for Namespaces.
+     */
+    public static final String NAMESPACE_LOGGER_NAME =
+            "javax.management.namespace";
+
+     /**
+     * Logger name for Namespaces.
+     */
+    public static final Logger NAMESPACE_LOGGER =
+            Logger.getLogger(NAMESPACE_LOGGER_NAME);
+
+    /**
      * Logger for Relation Service.
      */
     public static final Logger RELATION_LOGGER =
--- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/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;
     }
+
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,547 @@
+/*
+ * 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 com.sun.jmx.interceptor;
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.namespace.JMXNamespace;
+
+/**
+ * A dispatcher that dispatches to MBeanServers.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+//
+// This is the base class for implementing dispatchers. We have two concrete
+// dispatcher implementations:
+//
+//   * A NamespaceDispatchInterceptor, which dispatch calls to existing
+//     namespace interceptors
+//   * A DomainDispatchInterceptor, which dispatch calls to existing domain
+//     interceptors.
+//
+// With the JMX Namespaces feature, the JMX MBeanServer is now structured
+// as follows:
+//
+// The JMX MBeanServer delegates to a NamespaceDispatchInterceptor,
+// which either dispatches to a namespace, or delegates to the
+// DomainDispatchInterceptor (if the object name contained no namespace).
+// The DomainDispatchInterceptor in turn either dispatches to a domain (if
+// there is a JMXDomain for that domain) or delegates to the
+// DefaultMBeanServerInterceptor (if there is no JMXDomain for that
+// domain). This makes the following picture:
+//
+//             JMX MBeanServer (outer shell)
+//                          |
+//                          |
+//              NamespaceDispatchInterceptor
+//                   /               \
+//     no namespace in object name?   \
+//                 /                   \
+//                /                   dispatch to namespace
+//         DomainDispatchInterceptor
+//              /              \
+//    no JMXDomain for domain?  \
+//            /                  \
+//           /                   dispatch to domain
+//  DefaultMBeanServerInterceptor
+//         /
+//   invoke locally registered MBean
+//
+//  The logic for maintaining a map of interceptors
+//  and dispatching to impacted interceptor, is implemented in this
+//  base class, which both NamespaceDispatchInterceptor and
+//  DomainDispatchInterceptor extend.
+//
+public abstract class DispatchInterceptor
+        <T extends MBeanServer, N extends JMXNamespace>
+        extends MBeanServerInterceptorSupport {
+
+    /**
+     * This is an abstraction which allows us to handle queryNames
+     * and queryMBeans with the same algorithm. There are some subclasses
+     * where we need to override both queryNames & queryMBeans to apply
+     * the same transformation (usually aggregation of results when
+     * several namespaces/domains are impacted) to both algorithms.
+     * Usually the only thing that varies between the algorithm of
+     * queryNames & the algorithm of queryMBean is the type of objects
+     * in the returned Set. By using a QueryInvoker we can implement the
+     * transformation only once and apply it to both queryNames &
+     * queryMBeans.
+     * @see QueryInterceptor below, and its subclass in
+     * {@link DomainDispatcher}.
+     **/
+    static abstract class QueryInvoker<T> {
+        abstract Set<T> query(MBeanServer mbs,
+                        ObjectName pattern, QueryExp query);
+    }
+
+    /**
+     * Used to perform queryNames. A QueryInvoker that invokes
+     * queryNames on an MBeanServer.
+     **/
+    final static QueryInvoker<ObjectName> queryNamesInvoker =
+            new QueryInvoker<ObjectName>() {
+        Set<ObjectName> query(MBeanServer mbs,
+                        ObjectName pattern, QueryExp query) {
+            return mbs.queryNames(pattern,query);
+        }
+    };
+
+    /**
+     * Used to perform queryMBeans. A QueryInvoker that invokes
+     * queryMBeans on an MBeanServer.
+     **/
+    final static QueryInvoker<ObjectInstance> queryMBeansInvoker =
+            new QueryInvoker<ObjectInstance>() {
+        Set<ObjectInstance> query(MBeanServer mbs,
+                        ObjectName pattern, QueryExp query) {
+            return mbs.queryMBeans(pattern,query);
+        }
+    };
+
+    /**
+     * We use this class to intercept queries.
+     * There's a special case for JMXNamespace MBeans, because
+     * "namespace//*:*" matches both "namespace//domain:k=v" and
+     * "namespace//:type=JMXNamespace".
+     * Therefore, queries may need to be forwarded to more than
+     * on interceptor and the results aggregated...
+     */
+     static class QueryInterceptor {
+        final MBeanServer wrapped;
+        QueryInterceptor(MBeanServer mbs) {
+            wrapped = mbs;
+        }
+        <X> Set<X> query(ObjectName pattern, QueryExp query,
+                QueryInvoker<X> invoker, MBeanServer server) {
+            return invoker.query(server, pattern, query);
+        }
+
+        public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
+            return query(pattern,query,queryNamesInvoker,wrapped);
+        }
+
+        public Set<ObjectInstance> queryMBeans(ObjectName pattern,
+                QueryExp query) {
+            return query(pattern,query,queryMBeansInvoker,wrapped);
+        }
+    }
+
+    // We don't need a ConcurrentHashMap here because getkeys() returns
+    // an array of keys. Therefore there's no risk to have a
+    // ConcurrentModificationException. We must however take into
+    // account the fact that there can be no interceptor for
+    // some of the returned keys if the map is being modified by
+    // another thread, or by a callback within the same thread...
+    // See getKeys() in this class and query() in DomainDispatcher.
+    //
+    private final Map<String,T> handlerMap =
+            Collections.synchronizedMap(
+            new HashMap<String,T>());
+
+    // The key at which an interceptor for accessing the named MBean can be
+    // found in the handlerMap. Note: there doesn't need to be an interceptor
+    // for that key in the Map.
+    //
+    public abstract String getHandlerKey(ObjectName name);
+
+    // Returns an interceptor for that name, or null if there's no interceptor
+    // for that name.
+    abstract MBeanServer getInterceptorOrNullFor(ObjectName name);
+
+    // Returns a QueryInterceptor for that pattern.
+    abstract QueryInterceptor getInterceptorForQuery(ObjectName pattern);
+
+    // Returns the ObjectName of the JMXNamespace (or JMXDomain) for that
+    // key (a namespace or a domain name).
+    abstract ObjectName getHandlerNameFor(String key)
+        throws MalformedObjectNameException;
+
+    // Creates an interceptor for the given key, name, JMXNamespace (or
+    // JMXDomain). Note: this will be either a NamespaceInterceptor
+    // wrapping a JMXNamespace, if this object is an instance of
+    // NamespaceDispatchInterceptor, or a DomainInterceptor wrapping a
+    // JMXDomain, if this object is an instance of DomainDispatchInterceptor.
+    abstract T createInterceptorFor(String key, ObjectName name,
+            N jmxNamespace, Queue<Runnable> postRegisterQueue);
+    //
+    // The next interceptor in the chain.
+    //
+    // For the NamespaceDispatchInterceptor, this the DomainDispatchInterceptor.
+    // For the DomainDispatchInterceptor, this is the
+    // DefaultMBeanServerInterceptor.
+    //
+    // The logic of when to invoke the next interceptor in the chain depends
+    // on the logic of the concrete dispatcher class.
+    //
+    // For instance, the NamespaceDispatchInterceptor invokes the next
+    // interceptor when the object name doesn't contain any namespace.
+    //
+    // On the other hand, the DomainDispatchInterceptor invokes the
+    // next interceptor when there's no interceptor for the accessed domain.
+    //
+    abstract MBeanServer getNextInterceptor();
+
+    // hook for cleanup in subclasses.
+    void interceptorReleased(T interceptor,
+            Queue<Runnable> postDeregisterQueue) {
+        // hook
+    }
+
+    // Hook for subclasses.
+    MBeanServer getInterceptorForCreate(ObjectName name)
+        throws MBeanRegistrationException {
+        final MBeanServer ns = getInterceptorOrNullFor(name);
+        if (ns == null) // name cannot be null here.
+            throw new MBeanRegistrationException(
+                    new IllegalArgumentException("No such MBean handler: " +
+                        getHandlerKey(name) + " for " +name));
+        return ns;
+    }
+
+    // Hook for subclasses.
+    MBeanServer getInterceptorForInstance(ObjectName name)
+        throws InstanceNotFoundException {
+        final MBeanServer ns = getInterceptorOrNullFor(name);
+        if (ns == null) // name cannot be null here.
+            throw new InstanceNotFoundException(String.valueOf(name));
+        return ns;
+    }
+
+    // sanity checks
+    void validateHandlerNameFor(String key, ObjectName name) {
+        if (key == null || key.equals(""))
+            throw new IllegalArgumentException("invalid key for "+name+": "+key);
+        try {
+            final ObjectName handlerName = getHandlerNameFor(key);
+            if (!name.equals(handlerName))
+                throw new IllegalArgumentException("bad handler name: "+name+
+                        ". Should be: "+handlerName);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(name.toString(),x);
+        }
+    }
+
+    // Called by the DefaultMBeanServerInterceptor when an instance
+    // of JMXNamespace (or a subclass of it) is registered as an MBean.
+    // This method is usually invoked from within the repository lock,
+    // hence the necessity of the postRegisterQueue.
+    public void addNamespace(ObjectName name, N jmxNamespace,
+            Queue<Runnable> postRegisterQueue) {
+        final String key = getHandlerKey(name);
+        validateHandlerNameFor(key,name);
+        synchronized (handlerMap) {
+            final T exists =
+                    handlerMap.get(key);
+            if (exists != null)
+                throw new IllegalArgumentException(key+
+                        ": handler already exists");
+
+            final T ns = createInterceptorFor(key,name,jmxNamespace,
+                    postRegisterQueue);
+            handlerMap.put(key,ns);
+        }
+    }
+
+    // Called by the DefaultMBeanServerInterceptor when an instance
+    // of JMXNamespace (or a subclass of it) is deregistered.
+    // This method is usually invoked from within the repository lock,
+    // hence the necessity of the postDeregisterQueue.
+    public void removeNamespace(ObjectName name, N jmxNamespace,
+            Queue<Runnable> postDeregisterQueue) {
+        final String key = getHandlerKey(name);
+        final T ns;
+        synchronized(handlerMap) {
+            ns = handlerMap.remove(key);
+        }
+        interceptorReleased(ns,postDeregisterQueue);
+    }
+
+    // Get the interceptor for that key.
+    T getInterceptor(String key) {
+        synchronized (handlerMap) {
+            return handlerMap.get(key);
+        }
+    }
+
+    // We return an array of keys, which makes it possible to make
+    // concurrent modifications of the handlerMap, provided that
+    // the code which loops over the keys is prepared to handle null
+    // interceptors.
+    // See declaration of handlerMap above, and see also query() in
+    // DomainDispatcher
+    //
+    public String[] getKeys() {
+        synchronized (handlerMap) {
+            final int size = handlerMap.size();
+            return handlerMap.keySet().toArray(new String[size]);
+        }
+    }
+
+    // From MBeanServer
+    public ObjectInstance createMBean(String className, ObjectName name)
+            throws ReflectionException, InstanceAlreadyExistsException,
+                   MBeanRegistrationException, MBeanException,
+                   NotCompliantMBeanException {
+        return getInterceptorForCreate(name).createMBean(className,name);
+    }
+
+    // From MBeanServer
+    public ObjectInstance createMBean(String className, ObjectName name,
+                                      ObjectName loaderName)
+            throws ReflectionException, InstanceAlreadyExistsException,
+                   MBeanRegistrationException, MBeanException,
+                   NotCompliantMBeanException, InstanceNotFoundException{
+        return getInterceptorForCreate(name).createMBean(className,name,loaderName);
+    }
+
+    // From MBeanServer
+    public ObjectInstance createMBean(String className, ObjectName name,
+                                      Object params[], String signature[])
+            throws ReflectionException, InstanceAlreadyExistsException,
+                   MBeanRegistrationException, MBeanException,
+                   NotCompliantMBeanException{
+        return getInterceptorForCreate(name).
+                createMBean(className,name,params,signature);
+    }
+
+    // From MBeanServer
+    public ObjectInstance createMBean(String className, ObjectName name,
+                                      ObjectName loaderName, Object params[],
+                                      String signature[])
+            throws ReflectionException, InstanceAlreadyExistsException,
+                   MBeanRegistrationException, MBeanException,
+                   NotCompliantMBeanException, InstanceNotFoundException{
+        return getInterceptorForCreate(name).createMBean(className,name,loaderName,
+                                                   params,signature);
+    }
+
+    // From MBeanServer
+    public ObjectInstance registerMBean(Object object, ObjectName name)
+            throws InstanceAlreadyExistsException, MBeanRegistrationException,
+                   NotCompliantMBeanException {
+        return getInterceptorForCreate(name).registerMBean(object,name);
+    }
+
+    // From MBeanServer
+    public void unregisterMBean(ObjectName name)
+            throws InstanceNotFoundException, MBeanRegistrationException {
+        getInterceptorForInstance(name).unregisterMBean(name);
+    }
+
+    // From MBeanServer
+    public ObjectInstance getObjectInstance(ObjectName name)
+            throws InstanceNotFoundException {
+        return getInterceptorForInstance(name).getObjectInstance(name);
+    }
+
+    // From MBeanServer
+    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+        final QueryInterceptor mbs =
+                getInterceptorForQuery(name);
+        if (mbs == null)  return Collections.emptySet();
+        else return mbs.queryMBeans(name,query);
+    }
+
+    // From MBeanServer
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        final QueryInterceptor mbs =
+                getInterceptorForQuery(name);
+        if (mbs == null)  return Collections.emptySet();
+        else return mbs.queryNames(name,query);
+    }
+
+    // From MBeanServer
+    public boolean isRegistered(ObjectName name) {
+        final MBeanServer mbs = getInterceptorOrNullFor(name);
+        if (mbs == null) return false;
+        else return mbs.isRegistered(name);
+    }
+
+    // From MBeanServer
+    public Integer getMBeanCount() {
+        return getNextInterceptor().getMBeanCount();
+    }
+
+    // From MBeanServer
+    public Object getAttribute(ObjectName name, String attribute)
+            throws MBeanException, AttributeNotFoundException,
+                   InstanceNotFoundException, ReflectionException {
+        return getInterceptorForInstance(name).getAttribute(name,attribute);
+    }
+
+    // From MBeanServer
+    public AttributeList getAttributes(ObjectName name, String[] attributes)
+            throws InstanceNotFoundException, ReflectionException {
+        return getInterceptorForInstance(name).getAttributes(name,attributes);
+    }
+
+    // From MBeanServer
+    public void setAttribute(ObjectName name, Attribute attribute)
+            throws InstanceNotFoundException, AttributeNotFoundException,
+                   InvalidAttributeValueException, MBeanException,
+                   ReflectionException {
+        getInterceptorForInstance(name).setAttribute(name,attribute);
+    }
+
+    // From MBeanServer
+    public AttributeList setAttributes(ObjectName name,
+                                       AttributeList attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        return getInterceptorForInstance(name).setAttributes(name,attributes);
+    }
+
+    // From MBeanServer
+    public Object invoke(ObjectName name, String operationName,
+                         Object params[], String signature[])
+            throws InstanceNotFoundException, MBeanException,
+                   ReflectionException {
+        return getInterceptorForInstance(name).invoke(name,operationName,params,
+                signature);
+    }
+
+    // From MBeanServer
+    public String getDefaultDomain() {
+        return getNextInterceptor().getDefaultDomain();
+    }
+
+    /**
+     * Returns the list of domains in which any MBean is currently
+     * registered.
+     */
+    public abstract String[] getDomains();
+
+    // From MBeanServer
+    public void addNotificationListener(ObjectName name,
+                                        NotificationListener listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+            throws InstanceNotFoundException {
+        getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
+                handback);
+    }
+
+
+    // From MBeanServer
+    public void addNotificationListener(ObjectName name,
+                                        ObjectName listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+            throws InstanceNotFoundException {
+        getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
+                handback);
+    }
+
+    // From MBeanServer
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        getInterceptorForInstance(name).removeNotificationListener(name,listener);
+    }
+
+    // From MBeanServer
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
+                handback);
+    }
+
+
+    // From MBeanServer
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        getInterceptorForInstance(name).removeNotificationListener(name,listener);
+    }
+
+    // From MBeanServer
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
+                handback);
+    }
+
+    // From MBeanServer
+    public MBeanInfo getMBeanInfo(ObjectName name)
+            throws InstanceNotFoundException, IntrospectionException,
+                   ReflectionException {
+        return getInterceptorForInstance(name).getMBeanInfo(name);
+    }
+
+
+    // From MBeanServer
+    public boolean isInstanceOf(ObjectName name, String className)
+            throws InstanceNotFoundException {
+        return getInterceptorForInstance(name).isInstanceOf(name,className);
+    }
+
+    // From MBeanServer
+    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+        throws InstanceNotFoundException {
+        return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName);
+    }
+
+    // From MBeanServer
+    public ClassLoader getClassLoader(ObjectName loaderName)
+        throws InstanceNotFoundException {
+        return getInterceptorForInstance(loaderName).getClassLoader(loaderName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,322 @@
+/*
+ * 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 com.sun.jmx.interceptor;
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.MBeanInstantiator;
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.namespace.DomainInterceptor;
+import java.util.Queue;
+import java.util.Set;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.namespace.JMXDomain;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+/**
+ * A dispatcher that dispatch incoming MBeanServer requests to
+ * DomainInterceptors.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+//
+// See comments in  DispatchInterceptor.
+//
+class DomainDispatchInterceptor
+        extends DispatchInterceptor<DomainInterceptor, JMXDomain> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    private static final ObjectName ALL_DOMAINS =
+            JMXDomain.getDomainObjectName("*");
+
+
+    /**
+     *  A QueryInterceptor that perform & aggregates queries spanning several
+     *  domains.
+     */
+    final static class AggregatingQueryInterceptor extends QueryInterceptor {
+
+        private final DomainDispatchInterceptor parent;
+        AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) {
+            super(dispatcher.localNamespace);
+            parent = dispatcher;
+        }
+
+        /**
+         * Perform queryNames or queryMBeans, depending on which QueryInvoker
+         * is passed as argument. This is closures without closures.
+         **/
+        @Override
+        <T> Set<T> query(ObjectName pattern, QueryExp query,
+                QueryInvoker<T> invoker, MBeanServer localNamespace) {
+            final Set<T> local = invoker.query(localNamespace, pattern, query);
+
+            // Add all matching MBeans from local namespace.
+            final Set<T> res = Util.cloneSet(local);
+
+            final boolean all = (pattern == null ||
+                    pattern.getDomain().equals("*"));
+            if (pattern == null) pattern = ObjectName.WILDCARD;
+
+            final String domain = pattern.getDomain();
+
+            // If there's no domain pattern, just include the pattern's domain.
+            // Otherwiae, loop over all virtual domains (parent.getKeys()).
+            final String[] keys =
+                (pattern.isDomainPattern() ?
+                    parent.getKeys() : new String[]{domain});
+
+            // Add all matching MBeans from each virtual domain
+            //
+            for (String key : keys) {
+                // Only invoke those virtual domain which are selected
+                // by the domain pattern
+                //
+                if (!all && !Util.isDomainSelected(key, domain))
+                    continue;
+
+                try {
+                    final MBeanServer mbs = parent.getInterceptor(key);
+
+                    // mbs can be null if the interceptor was removed
+                    // concurrently...
+                    // See handlerMap and getKeys() in DispatchInterceptor
+                    //
+                    if (mbs == null) continue;
+
+                    // If the domain is selected, we can replace the pattern
+                    // by the actual domain. This is safer if we want to avoid
+                    // a domain (which could be backed up by an MBeanServer) to
+                    // return names from outside the domain.
+                    // So instead of asking the domain handler for "foo" to
+                    // return all names which match "?o*:type=Bla,*" we're
+                    // going to ask it to return all names which match
+                    // "foo:type=Bla,*"
+                    //
+                    final ObjectName subPattern = pattern.withDomain(key);
+                    res.addAll(invoker.query(mbs, subPattern, query));
+                } catch (Exception x) {
+                    LOG.finest("Ignoring exception " +
+                            "when attempting to query namespace "+key+": "+x);
+                    continue;
+                }
+            }
+            return res;
+        }
+    }
+
+    private final DefaultMBeanServerInterceptor localNamespace;
+    private final String mbeanServerName;
+    private final MBeanServerDelegate delegate;
+
+    /**
+     * Creates a DomainDispatchInterceptor with the specified
+     * repository instance.
+     *
+     * @param outer A pointer to the MBeanServer object that must be
+     *        passed to the MBeans when invoking their
+     *        {@link javax.management.MBeanRegistration} interface.
+     * @param delegate A pointer to the MBeanServerDelegate associated
+     *        with the new MBeanServer. The new MBeanServer must register
+     *        this MBean in its MBean repository.
+     * @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
+     */
+    public DomainDispatchInterceptor(MBeanServer         outer,
+                            MBeanServerDelegate delegate,
+                            MBeanInstantiator   instantiator,
+                            Repository          repository,
+                            NamespaceDispatchInterceptor namespaces)  {
+           localNamespace = new DefaultMBeanServerInterceptor(outer,
+                   delegate, instantiator,repository,namespaces);
+           mbeanServerName = Util.getMBeanServerSecurityName(delegate);
+           this.delegate = delegate;
+    }
+
+    final boolean isLocalHandlerNameFor(String domain,
+            ObjectName handlerName) {
+        if (domain == null) return true;
+        return handlerName.getDomain().equals(domain) &&
+               JMXDomain.TYPE_ASSIGNMENT.equals(
+               handlerName.getKeyPropertyListString());
+    }
+
+    @Override
+    void validateHandlerNameFor(String key, ObjectName name) {
+        super.validateHandlerNameFor(key,name);
+        final String[] domains = localNamespace.getDomains();
+        for (int i=0;i<domains.length;i++) {
+            if (domains[i].equals(key))
+                throw new IllegalArgumentException("domain "+key+
+                        " is not empty");
+        }
+    }
+
+    @Override
+    final MBeanServer getInterceptorOrNullFor(ObjectName name) {
+        if (name == null) return localNamespace;
+        final String domain = name.getDomain();
+        if (domain.endsWith(NAMESPACE_SEPARATOR)) return localNamespace;
+        if (domain.contains(NAMESPACE_SEPARATOR)) return null;
+        final String localDomain = domain;
+        if (isLocalHandlerNameFor(localDomain,name)) {
+            LOG.finer("dispatching to local namespace");
+            return localNamespace;
+        }
+        final DomainInterceptor ns = getInterceptor(localDomain);
+        if (ns == null) {
+            if (LOG.isLoggable(Level.FINER)) {
+                LOG.finer("dispatching to local namespace: " + localDomain);
+            }
+            return getNextInterceptor();
+        }
+        if (LOG.isLoggable(Level.FINER)) {
+            LOG.finer("dispatching to domain: " + localDomain);
+        }
+        return ns;
+    }
+
+    private boolean multipleQuery(ObjectName pattern) {
+        if (pattern == null) return true;
+        if (pattern.isDomainPattern()) return true;
+
+        try {
+            // This is a bit of a hack. If there's any chance that a JMXDomain
+            // MBean name is selected by the given pattern then we must include
+            // the local namespace in our search.
+            // Returning true will have this effect.
+            if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain())))
+                return true;
+        } catch (MalformedObjectNameException x) {
+            // should not happen
+            throw new IllegalArgumentException(String.valueOf(pattern), x);
+        }
+        return false;
+    }
+
+    @Override
+    final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
+
+        // Check if we need to aggregate.
+        if (multipleQuery(pattern))
+            return new AggregatingQueryInterceptor(this);
+
+        // We don't need to aggregate: do the "simple" thing...
+        final String domain = pattern.getDomain();
+
+        // Do we have a virtual domain?
+        final DomainInterceptor ns = getInterceptor(domain);
+        if (ns != null) {
+            if (LOG.isLoggable(Level.FINER))
+                LOG.finer("dispatching to domain: " + domain);
+            return new QueryInterceptor(ns);
+        }
+
+        // We don't have a virtual domain. Send to local domains.
+        if (LOG.isLoggable(Level.FINER))
+             LOG.finer("dispatching to local namespace: " + domain);
+        return new QueryInterceptor(localNamespace);
+    }
+
+    @Override
+    final ObjectName getHandlerNameFor(String key)
+        throws MalformedObjectNameException {
+        return JMXDomain.getDomainObjectName(key);
+    }
+
+    @Override
+    final public String getHandlerKey(ObjectName name) {
+        return name.getDomain();
+    }
+
+    @Override
+    final DomainInterceptor createInterceptorFor(String key,
+            ObjectName name, JMXDomain handler,
+            Queue<Runnable> postRegisterQueue) {
+        final DomainInterceptor ns =
+                new DomainInterceptor(mbeanServerName,handler,key);
+        ns.addPostRegisterTask(postRegisterQueue, delegate);
+        if (LOG.isLoggable(Level.FINER)) {
+            LOG.finer("DomainInterceptor created: "+ns);
+        }
+        return ns;
+    }
+
+    @Override
+    final void interceptorReleased(DomainInterceptor interceptor,
+            Queue<Runnable> postDeregisterQueue) {
+        interceptor.addPostDeregisterTask(postDeregisterQueue, delegate);
+    }
+
+    @Override
+    final DefaultMBeanServerInterceptor getNextInterceptor() {
+        return localNamespace;
+    }
+
+    /**
+     * Returns the list of domains in which any MBean is currently
+     * registered.
+     */
+    @Override
+    public String[] getDomains() {
+        // A JMXDomain is registered in its own domain.
+        // Therefore, localNamespace.getDomains() contains all domains.
+        // In addition, localNamespace will perform the necessary
+        // MBeanPermission checks for getDomains().
+        //
+        return localNamespace.getDomains();
+    }
+
+    /**
+     * Returns the number of MBeans registered in the MBean server.
+     */
+    @Override
+    public Integer getMBeanCount() {
+        int count = getNextInterceptor().getMBeanCount().intValue();
+        final String[] keys = getKeys();
+        for (String key:keys) {
+            final MBeanServer mbs = getInterceptor(key);
+            if (mbs == null) continue;
+            count += mbs.getMBeanCount().intValue();
+        }
+        return Integer.valueOf(count);
+    }
+}
--- a/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2002-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
@@ -25,35 +25,14 @@
 
 package com.sun.jmx.interceptor;
 
-import java.util.Set;
 
-// RI import
-import javax.management.DynamicMBean;
-import javax.management.AttributeNotFoundException;
+import java.io.ObjectInputStream;
+import javax.management.InstanceNotFoundException;
 import javax.management.MBeanException;
-import javax.management.ReflectionException;
-import javax.management.MBeanAttributeInfo;
-import javax.management.MBeanInfo;
-import javax.management.QueryExp;
-import javax.management.NotificationListener;
-import javax.management.NotificationFilter;
-import javax.management.ListenerNotFoundException;
-import javax.management.IntrospectionException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
 import javax.management.OperationsException;
-import javax.management.MBeanNotificationInfo;
-import javax.management.JMRuntimeException;
-import javax.management.InstanceNotFoundException;
-import javax.management.NotCompliantMBeanException;
-import javax.management.MBeanRegistrationException;
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InvalidAttributeValueException;
-import javax.management.ObjectName;
-import javax.management.ObjectInstance;
-import javax.management.Attribute;
-import javax.management.AttributeList;
-import javax.management.RuntimeOperationsException;
-import javax.management.MBeanServerConnection;
-import javax.management.MBeanServerDelegate;
+import javax.management.ReflectionException;
 import javax.management.loading.ClassLoaderRepository;
 
 /**
@@ -85,618 +64,67 @@
  *
  * @since 1.5
  */
-public interface MBeanServerInterceptor extends MBeanServerConnection {
+public interface MBeanServerInterceptor extends MBeanServer {
     /**
-     * Instantiates and registers an MBean in the MBean server.  The
-     * MBean server will use its {@link
-     * javax.management.loading.ClassLoaderRepository Default Loader
-     * Repository} to load the class of the MBean.  An object name is
-     * associated to the MBean.  If the object name given is null, the
-     * MBean must provide its own name by implementing the {@link
-     * javax.management.MBeanRegistration MBeanRegistration} interface
-     * and returning the name from the {@link
-     * javax.management.MBeanRegistration#preRegister preRegister} method.
-     *
-     * @param className The class name of the MBean to be instantiated.
-     * @param name The object name of the MBean. May be null.
-     * @param params An array containing the parameters of the
-     * constructor to be invoked.
-     * @param signature An array containing the signature of the
-     * constructor to be invoked.
-     *
-     * @return An <CODE>ObjectInstance</CODE>, containing the
-     * <CODE>ObjectName</CODE> and the Java class name of the newly
-     * instantiated MBean.
-     *
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.ClassNotFoundException</CODE> or a
-     * <CODE>java.lang.Exception</CODE> that occurred when trying to
-     * invoke the MBean's constructor.
-     * @exception InstanceAlreadyExistsException The MBean is already
-     * under the control of the MBean server.
-     * @exception MBeanRegistrationException The
-     * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
-     * interface) method of the MBean has thrown an exception. The
-     * MBean will not be registered.
-     * @exception MBeanException The constructor of the MBean has
-     * thrown an exception
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The className
-     * passed in parameter is null, the <CODE>ObjectName</CODE> passed
-     * in parameter contains a pattern or no <CODE>ObjectName</CODE>
-     * is specified for the MBean.
+     * This method should never be called.
+     * Usually hrows UnsupportedOperationException.
      */
-    public ObjectInstance createMBean(String className, ObjectName name,
-                                      Object params[], String signature[])
-            throws ReflectionException, InstanceAlreadyExistsException,
-                   MBeanRegistrationException, MBeanException,
-                   NotCompliantMBeanException;
-
+    public Object instantiate(String className)
+            throws ReflectionException, MBeanException;
     /**
-     * Instantiates and registers an MBean in the MBean server.  The
-     * class loader to be used is identified by its object name. An
-     * object name is associated to the MBean. If the object name of
-     * the loader is not specified, the ClassLoader that loaded the
-     * MBean server will be used.  If the MBean object name given is
-     * null, the MBean must provide its own name by implementing the
-     * {@link javax.management.MBeanRegistration MBeanRegistration}
-     * interface and returning the name from the {@link
-     * javax.management.MBeanRegistration#preRegister preRegister} method.
-     *
-     * @param className The class name of the MBean to be instantiated.
-     * @param name The object name of the MBean. May be null.
-     * @param params An array containing the parameters of the
-     * constructor to be invoked.
-     * @param signature An array containing the signature of the
-     * constructor to be invoked.
-     * @param loaderName The object name of the class loader to be used.
-     *
-     * @return An <CODE>ObjectInstance</CODE>, containing the
-     * <CODE>ObjectName</CODE> and the Java class name of the newly
-     * instantiated MBean.
-     *
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.ClassNotFoundException</CODE> or a
-     * <CODE>java.lang.Exception</CODE> that occurred when trying to
-     * invoke the MBean's constructor.
-     * @exception InstanceAlreadyExistsException The MBean is already
-     * under the control of the MBean server.
-     * @exception MBeanRegistrationException The
-     * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
-     * interface) method of the MBean has thrown an exception. The
-     * MBean will not be registered.
-     * @exception MBeanException The constructor of the MBean has
-     * thrown an exception
-     * @exception InstanceNotFoundException The specified class loader
-     * is not registered in the MBean server.
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The className
-     * passed in parameter is null, the <CODE>ObjectName</CODE> passed
-     * in parameter contains a pattern or no <CODE>ObjectName</CODE>
-     * is specified for the MBean.
-     *
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public ObjectInstance createMBean(String className, ObjectName name,
-                                      ObjectName loaderName, Object params[],
-                                      String signature[])
-            throws ReflectionException, InstanceAlreadyExistsException,
-                   MBeanRegistrationException, MBeanException,
-                   NotCompliantMBeanException, InstanceNotFoundException;
-
+    public Object instantiate(String className, ObjectName loaderName)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException;
     /**
-     * Registers a pre-existing object as an MBean with the MBean
-     * server. If the object name given is null, the MBean must
-     * provide its own name by implementing the {@link
-     * javax.management.MBeanRegistration MBeanRegistration} interface
-     * and returning the name from the {@link
-     * javax.management.MBeanRegistration#preRegister preRegister} method.
-     *
-     * @param object The  MBean to be registered as an MBean.
-     * @param name The object name of the MBean. May be null.
-     *
-     * @return The <CODE>ObjectInstance</CODE> for the MBean that has
-     * been registered.
-     *
-     * @exception InstanceAlreadyExistsException The MBean is already
-     * under the control of the MBean server.
-     * @exception MBeanRegistrationException The
-     * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
-     * interface) method of the MBean has thrown an exception. The
-     * MBean will not be registered.
-     * @exception NotCompliantMBeanException This object is not a JMX
-     * compliant MBean
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * passed in parameter is null or no object name is specified.
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public ObjectInstance registerMBean(Object object, ObjectName name)
-            throws InstanceAlreadyExistsException, MBeanRegistrationException,
-                   NotCompliantMBeanException;
+    public Object instantiate(String className, Object[] params,
+            String[] signature) throws ReflectionException, MBeanException;
 
     /**
-     * Unregisters an MBean from the MBean server. The MBean is
-     * identified by its object name. Once the method has been
-     * invoked, the MBean may no longer be accessed by its object
-     * name.
-     *
-     * @param name The object name of the MBean to be unregistered.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception MBeanRegistrationException The preDeregister
-     * ((<CODE>MBeanRegistration</CODE> interface) method of the MBean
-     * has thrown an exception.
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null or the MBean you are when trying to
-     * unregister is the {@link javax.management.MBeanServerDelegate
-     * MBeanServerDelegate} MBean.
-     *
-     */
-    public void unregisterMBean(ObjectName name)
-            throws InstanceNotFoundException, MBeanRegistrationException;
-
-    /**
-     * Gets the <CODE>ObjectInstance</CODE> for a given MBean
-     * registered with the MBean server.
-     *
-     * @param name The object name of the MBean.
-     *
-     * @return The <CODE>ObjectInstance</CODE> associated to the MBean
-     * specified by <VAR>name</VAR>.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     */
-    public ObjectInstance getObjectInstance(ObjectName name)
-            throws InstanceNotFoundException;
-
-    /**
-     * Gets MBeans controlled by the MBean server. This method allows
-     * any of the following to be obtained: All MBeans, a set of
-     * MBeans specified by pattern matching on the
-     * <CODE>ObjectName</CODE> and/or a Query expression, a specific
-     * MBean. When the object name is null or no domain and key
-     * properties are specified, all objects are to be selected (and
-     * filtered if a query is specified). It returns the set of
-     * <CODE>ObjectInstance</CODE> objects (containing the
-     * <CODE>ObjectName</CODE> and the Java Class name) for the
-     * selected MBeans.
-     *
-     * @param name The object name pattern identifying the MBeans to
-     * be retrieved. If null or no domain and key properties are
-     * specified, all the MBeans registered will be retrieved.
-     * @param query The query expression to be applied for selecting
-     * MBeans. If null no query expression will be applied for
-     * selecting MBeans.
-     *
-     * @return A set containing the <CODE>ObjectInstance</CODE>
-     * objects for the selected MBeans.  If no MBean satisfies the
-     * query an empty list is returned.
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query);
-
-    /**
-     * Gets the names of MBeans controlled by the MBean server. This
-     * method enables any of the following to be obtained: The names
-     * of all MBeans, the names of a set of MBeans specified by
-     * pattern matching on the <CODE>ObjectName</CODE> and/or a Query
-     * expression, a specific MBean name (equivalent to testing
-     * whether an MBean is registered). When the object name is null
-     * or no domain and key properties are specified, all objects are
-     * selected (and filtered if a query is specified). It returns the
-     * set of ObjectNames for the MBeans selected.
-     *
-     * @param name The object name pattern identifying the MBean names
-     * to be retrieved. If null oror no domain and key properties are
-     * specified, the name of all registered MBeans will be retrieved.
-     * @param query The query expression to be applied for selecting
-     * MBeans. If null no query expression will be applied for
-     * selecting MBeans.
-     *
-     * @return A set containing the ObjectNames for the MBeans
-     * selected.  If no MBean satisfies the query, an empty list is
-     * returned.
-     */
-    public Set<ObjectName> queryNames(ObjectName name, QueryExp query);
-
-    /**
-     * Checks whether an MBean, identified by its object name, is
-     * already registered with the MBean server.
-     *
-     * @param name The object name of the MBean to be checked.
-     *
-     * @return True if the MBean is already registered in the MBean
-     * server, false otherwise.
-     *
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null.
-     */
-    public boolean isRegistered(ObjectName name);
-
-    /**
-     * Returns the number of MBeans registered in the MBean server.
-     */
-    public Integer getMBeanCount();
-
-    /**
-     * Gets the value of a specific attribute of a named MBean. The MBean
-     * is identified by its object name.
-     *
-     * @param name The object name of the MBean from which the
-     * attribute is to be retrieved.
-     * @param attribute A String specifying the name of the attribute
-     * to be retrieved.
-     *
-     * @return  The value of the retrieved attribute.
-     *
-     * @exception AttributeNotFoundException The attribute specified
-     * is not accessible in the MBean.
-     * @exception MBeanException Wraps an exception thrown by the
-     * MBean's getter.
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.Exception</CODE> thrown when trying to invoke
-     * the setter.
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null or the attribute in parameter is
-     * null.
-     */
-    public Object getAttribute(ObjectName name, String attribute)
-            throws MBeanException, AttributeNotFoundException,
-                   InstanceNotFoundException, ReflectionException;
-
-    /**
-     * Enables the values of several attributes of a named MBean. The MBean
-     * is identified by its object name.
-     *
-     * @param name The object name of the MBean from which the
-     * attributes are retrieved.
-     * @param attributes A list of the attributes to be retrieved.
-     *
-     * @return The list of the retrieved attributes.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception ReflectionException An exception occurred when
-     * trying to invoke the getAttributes method of a Dynamic MBean.
-     * @exception RuntimeOperationsException Wrap a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null or attributes in parameter is null.
-     */
-    public AttributeList getAttributes(ObjectName name, String[] attributes)
-            throws InstanceNotFoundException, ReflectionException;
+    public Object instantiate(String className, ObjectName loaderName,
+            Object[] params, String[] signature)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException;
 
     /**
-     * Sets the value of a specific attribute of a named MBean. The MBean
-     * is identified by its object name.
-     *
-     * @param name The name of the MBean within which the attribute is
-     * to be set.
-     * @param attribute The identification of the attribute to be set
-     * and the value it is to be set to.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception AttributeNotFoundException The attribute specified
-     * is not accessible in the MBean.
-     * @exception InvalidAttributeValueException The value specified
-     * for the attribute is not valid.
-     * @exception MBeanException Wraps an exception thrown by the
-     * MBean's setter.
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.Exception</CODE> thrown when trying to invoke
-     * the setter.
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null or the attribute in parameter is
-     * null.
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public void setAttribute(ObjectName name, Attribute attribute)
-            throws InstanceNotFoundException, AttributeNotFoundException,
-                   InvalidAttributeValueException, MBeanException,
-                   ReflectionException;
-
-
-
-    /**
-     * Sets the values of several attributes of a named MBean. The MBean is
-     * identified by its object name.
-     *
-     * @param name The object name of the MBean within which the
-     * attributes are to be set.
-     * @param attributes A list of attributes: The identification of
-     * the attributes to be set and the values they are to be set to.
-     *
-     * @return The list of attributes that were set, with their new
-     * values.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception ReflectionException An exception occurred when
-     * trying to invoke the getAttributes method of a Dynamic MBean.
-     * @exception RuntimeOperationsException Wraps a
-     * <CODE>java.lang.IllegalArgumentException</CODE>: The object
-     * name in parameter is null or attributes in parameter is null.
-     */
-    public AttributeList setAttributes(ObjectName name,
-                                       AttributeList attributes)
-        throws InstanceNotFoundException, ReflectionException;
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+            throws InstanceNotFoundException, OperationsException;
 
     /**
-     * Invokes an operation on an MBean.
-     *
-     * @param name The object name of the MBean on which the method is
-     * to be invoked.
-     * @param operationName The name of the operation to be invoked.
-     * @param params An array containing the parameters to be set when
-     * the operation is invoked
-     * @param signature An array containing the signature of the
-     * operation. The class objects will be loaded using the same
-     * class loader as the one used for loading the MBean on which the
-     * operation was invoked.
-     *
-     * @return The object returned by the operation, which represents
-     * the result ofinvoking the operation on the MBean specified.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     * @exception MBeanException Wraps an exception thrown by the
-     * MBean's invoked method.
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.Exception</CODE> thrown while trying to invoke
-     * the method.
-     */
-    public Object invoke(ObjectName name, String operationName,
-                         Object params[], String signature[])
-            throws InstanceNotFoundException, MBeanException,
-                   ReflectionException;
-
-    /**
-     * Returns the default domain used for naming the MBean.
-     * The default domain name is used as the domain part in the ObjectName
-     * of MBeans if no domain is specified by the user.
-     */
-    public String getDefaultDomain();
-
-    /**
-     * Returns the list of domains in which any MBean is currently
-     * registered.
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public String[] getDomains();
-
-    /**
-     * <p>Adds a listener to a registered MBean.</p>
-     *
-     * <P> A notification emitted by an MBean will be forwarded by the
-     * MBeanServer to the listener.  If the source of the notification
-     * is a reference to an MBean object, the MBean server will replace it
-     * by that MBean's ObjectName.  Otherwise the source is unchanged.
-     *
-     * @param name The name of the MBean on which the listener should
-     * be added.
-     * @param listener The listener object which will handle the
-     * notifications emitted by the registered MBean.
-     * @param filter The filter object. If filter is null, no
-     * filtering will be performed before handling notifications.
-     * @param handback The context to be sent to the listener when a
-     * notification is emitted.
-     *
-     * @exception InstanceNotFoundException The MBean name provided
-     * does not match any of the registered MBeans.
-     */
-    public void addNotificationListener(ObjectName name,
-                                        NotificationListener listener,
-                                        NotificationFilter filter,
-                                        Object handback)
-            throws InstanceNotFoundException;
-
-
-    /**
-     * <p>Adds a listener to a registered MBean.</p>
-     *
-     * <p>A notification emitted by an MBean will be forwarded by the
-     * MBeanServer to the listener.  If the source of the notification
-     * is a reference to an MBean object, the MBean server will
-     * replace it by that MBean's ObjectName.  Otherwise the source is
-     * unchanged.</p>
-     *
-     * <p>The listener object that receives notifications is the one
-     * that is registered with the given name at the time this method
-     * is called.  Even if it is subsequently unregistered, it will
-     * continue to receive notifications.</p>
-     *
-     * @param name The name of the MBean on which the listener should
-     * be added.
-     * @param listener The object name of the listener which will
-     * handle the notifications emitted by the registered MBean.
-     * @param filter The filter object. If filter is null, no
-     * filtering will be performed before handling notifications.
-     * @param handback The context to be sent to the listener when a
-     * notification is emitted.
-     *
-     * @exception InstanceNotFoundException The MBean name of the
-     * notification listener or of the notification broadcaster does
-     * not match any of the registered MBeans.
-     * @exception RuntimeOperationsException Wraps an {@link
-     * IllegalArgumentException}.  The MBean named by
-     * <code>listener</code> exists but does not implement the {@link
-     * NotificationListener} interface.
-     * @exception IOException A communication problem occurred when
-     * talking to the MBean server.
-     */
-    public void addNotificationListener(ObjectName name,
-                                        ObjectName listener,
-                                        NotificationFilter filter,
-                                        Object handback)
-            throws InstanceNotFoundException;
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+            throws OperationsException, ReflectionException;
 
     /**
-     * Removes a listener from a registered MBean.
-     *
-     * <P> If the listener is registered more than once, perhaps with
-     * different filters or callbacks, this method will remove all
-     * those registrations.
-     *
-     * @param name The name of the MBean on which the listener should
-     * be removed.
-     * @param listener The object name of the listener to be removed.
-     *
-     * @exception InstanceNotFoundException The MBean name provided
-     * does not match any of the registered MBeans.
-     * @exception ListenerNotFoundException The listener is not
-     * registered in the MBean.
+     * This method should never be called.
+     * Usually hrows UnsupportedOperationException.
      */
-    public void removeNotificationListener(ObjectName name,
-                                           ObjectName listener)
-        throws InstanceNotFoundException, ListenerNotFoundException;
-
-    /**
-     * <p>Removes a listener from a registered MBean.</p>
-     *
-     * <p>The MBean must have a listener that exactly matches the
-     * given <code>listener</code>, <code>filter</code>, and
-     * <code>handback</code> parameters.  If there is more than one
-     * such listener, only one is removed.</p>
-     *
-     * <p>The <code>filter</code> and <code>handback</code> parameters
-     * may be null if and only if they are null in a listener to be
-     * removed.</p>
-     *
-     * @param name The name of the MBean on which the listener should
-     * be removed.
-     * @param listener A listener that was previously added to this
-     * MBean.
-     * @param filter The filter that was specified when the listener
-     * was added.
-     * @param handback The handback that was specified when the
-     * listener was added.
-     *
-     * @exception InstanceNotFoundException The MBean name provided
-     * does not match any of the registered MBeans.
-     * @exception ListenerNotFoundException The listener is not
-     * registered in the MBean, or it is not registered with the given
-     * filter and handback.
-     */
-    public void removeNotificationListener(ObjectName name,
-                                           ObjectName listener,
-                                           NotificationFilter filter,
-                                           Object handback)
-            throws InstanceNotFoundException, ListenerNotFoundException;
-
-
-    /**
-     * <p>Removes a listener from a registered MBean.</p>
-     *
-     * <P> If the listener is registered more than once, perhaps with
-     * different filters or callbacks, this method will remove all
-     * those registrations.
-     *
-     * @param name The name of the MBean on which the listener should
-     * be removed.
-     * @param listener The listener object which will handle the
-     * notifications emitted by the registered MBean.
-     *
-     * @exception InstanceNotFoundException The MBean name provided
-     * does not match any of the registered MBeans.
-     * @exception ListenerNotFoundException The listener is not
-     * registered in the MBean.
-     */
-    public void removeNotificationListener(ObjectName name,
-                                           NotificationListener listener)
-            throws InstanceNotFoundException, ListenerNotFoundException;
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+            ObjectName loaderName, byte[] data)
+            throws InstanceNotFoundException, OperationsException,
+            ReflectionException;
 
     /**
-     * <p>Removes a listener from a registered MBean.</p>
-     *
-     * <p>The MBean must have a listener that exactly matches the
-     * given <code>listener</code>, <code>filter</code>, and
-     * <code>handback</code> parameters.  If there is more than one
-     * such listener, only one is removed.</p>
-     *
-     * <p>The <code>filter</code> and <code>handback</code> parameters
-     * may be null if and only if they are null in a listener to be
-     * removed.</p>
-     *
-     * @param name The name of the MBean on which the listener should
-     * be removed.
-     * @param listener A listener that was previously added to this
-     * MBean.
-     * @param filter The filter that was specified when the listener
-     * was added.
-     * @param handback The handback that was specified when the
-     * listener was added.
-     *
-     * @exception InstanceNotFoundException The MBean name provided
-     * does not match any of the registered MBeans.
-     * @exception ListenerNotFoundException The listener is not
-     * registered in the MBean, or it is not registered with the given
-     * filter and handback.
+     * This method should never be called.
+     * Usually throws UnsupportedOperationException.
      */
-    public void removeNotificationListener(ObjectName name,
-                                           NotificationListener listener,
-                                           NotificationFilter filter,
-                                           Object handback)
-            throws InstanceNotFoundException, ListenerNotFoundException;
-
-    /**
-     * This method discovers the attributes and operations that an
-     * MBean exposes for management.
-     *
-     * @param name The name of the MBean to analyze
-     *
-     * @return An instance of <CODE>MBeanInfo</CODE> allowing the
-     * retrieval of all attributes and operations of this MBean.
-     *
-     * @exception IntrospectionException An exception occurred during
-     * introspection.
-     * @exception InstanceNotFoundException The MBean specified was
-     * not found.
-     * @exception ReflectionException An exception occurred when
-     * trying to invoke the getMBeanInfo of a Dynamic MBean.
-     */
-    public MBeanInfo getMBeanInfo(ObjectName name)
-            throws InstanceNotFoundException, IntrospectionException,
-                   ReflectionException;
-
-
-    /**
-     * Returns true if the MBean specified is an instance of the
-     * specified class, false otherwise.
-     *
-     * @param name The <CODE>ObjectName</CODE> of the MBean.
-     * @param className The name of the class.
-     *
-     * @return true if the MBean specified is an instance of the
-     * specified class, false otherwise.
-     *
-     * @exception InstanceNotFoundException The MBean specified is not
-     * registered in the MBean server.
-     */
-    public boolean isInstanceOf(ObjectName name, String className)
-            throws InstanceNotFoundException;
-
-    /**
-     * <p>Return the {@link java.lang.ClassLoader} that was used for
-     * loading the class of the named MBean.
-     * @param mbeanName The ObjectName of the MBean.
-     * @return The ClassLoader used for that MBean.
-     * @exception InstanceNotFoundException if the named MBean is not found.
-     */
-    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
-        throws InstanceNotFoundException;
-
-    /**
-     * <p>Return the named {@link java.lang.ClassLoader}.
-     * @param loaderName The ObjectName of the ClassLoader.
-     * @return The named ClassLoader.
-     * @exception InstanceNotFoundException if the named ClassLoader is
-     * not found.
-     */
-    public ClassLoader getClassLoader(ObjectName loaderName)
-        throws InstanceNotFoundException;
+    public ClassLoaderRepository getClassLoaderRepository();
 
 }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,127 @@
+/*
+ * 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 com.sun.jmx.interceptor;
+
+import java.io.ObjectInputStream;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.ReflectionException;
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * An abstract class for MBeanServerInterceptorSupport.
+ * Some methods in MBeanServerInterceptor should never be called.
+ * This base class provides an implementation of these methods that simply
+ * throw an {@link UnsupportedOperationException}.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public abstract class MBeanServerInterceptorSupport
+        implements MBeanServerInterceptor {
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className)
+            throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, ObjectName loaderName)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, Object[] params,
+            String[] signature) throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, ObjectName loaderName,
+            Object[] params, String[] signature)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+            throws InstanceNotFoundException, OperationsException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+            throws OperationsException, ReflectionException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+            ObjectName loaderName, byte[] data)
+            throws InstanceNotFoundException, OperationsException,
+            ReflectionException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public ClassLoaderRepository getClassLoaderRepository() {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+}
--- a/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java	Wed Sep 03 14:31:17 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1341 +0,0 @@
-/*
- * Copyright 2007 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 com.sun.jmx.interceptor;
-
-import com.sun.jmx.mbeanserver.Util;
-import java.io.ObjectInputStream;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-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.MBeanException;
-import javax.management.MBeanInfo;
-import javax.management.MBeanRegistrationException;
-import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.NotCompliantMBeanException;
-import javax.management.NotificationBroadcaster;
-import javax.management.NotificationEmitter;
-import javax.management.NotificationFilter;
-import javax.management.NotificationListener;
-import javax.management.ObjectInstance;
-import javax.management.ObjectName;
-import javax.management.OperationsException;
-import javax.management.QueryEval;
-import javax.management.QueryExp;
-import javax.management.ReflectionException;
-import javax.management.RuntimeOperationsException;
-import javax.management.loading.ClassLoaderRepository;
-
-/**
- * <p>Base class for custom implementations of the {@link MBeanServer}
- * interface. The commonest use of this class is as the {@linkplain
- * JMXNamespace#getSourceServer() source server} for a {@link
- * JMXNamespace}, although this class can be used anywhere an {@code
- * MBeanServer} instance is required. Note that the usual ways to
- * obtain an {@code MBeanServer} instance are either to use {@link
- * java.lang.management.ManagementFactory#getPlatformMBeanServer()
- * ManagementFactory.getPlatformMBeanServer()} or to use the {@code
- * newMBeanServer} or {@code createMBeanServer} methods from {@link
- * javax.management.MBeanServerFactory MBeanServerFactory}. {@code
- * MBeanServerSupport} is for certain cases where those are not
- * appropriate.</p>
- *
- * <p>There are two main use cases for this class: <a
- * href="#special-purpose">special-purpose MBeanServer implementations</a>,
- * and <a href="#virtual">namespaces containing Virtual MBeans</a>. The next
- * sections explain these use cases.</p>
- *
- * <p>In the simplest case, a subclass needs to implement only two methods:</p>
- *
- * <ul>
- *     <li>
- *         {@link #getNames getNames} which returns the name of
- *         all MBeans handled by this {@code MBeanServer}.
- *     </li>
- *     <li>
- *         {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a
- *         {@link DynamicMBean} that can be used to invoke operations and
- *         obtain meta data (MBeanInfo) on a given MBean.
- *     </li>
- * </ul>
- *
- * <p>Subclasses can create such {@link DynamicMBean} MBeans on the fly - for
- * instance, using the class {@link javax.management.StandardMBean}, just for
- * the duration of an MBeanServer method call.</p>
- *
- * <h4 id="special-purpose">Special-purpose MBeanServer implementations</h4>
- *
- * <p>In some cases
- * the general-purpose {@code MBeanServer} that you get from
- * {@link javax.management.MBeanServerFactory MBeanServerFactory} is not
- * appropriate.  You might need different security checks, or you might
- * want a mock {@code MBeanServer} suitable for use in tests, or you might
- * want a simplified and optimized {@code MBeanServer} for a special purpose.</p>
- *
- * <p>As an example of a special-purpose {@code MBeanServer}, the class {@link
- * javax.management.QueryNotificationFilter QueryNotificationFilter} constructs
- * an {@code MBeanServer} instance every time it filters a notification,
- * with just one MBean that represents the notification. Although it could
- * use {@code MBeanServerFactory.newMBeanServer}, a special-purpose {@code
- * MBeanServer} will be quicker to create, use less memory, and have simpler
- * methods that execute faster.</p>
- *
- * <p>Here is an example of a special-purpose {@code MBeanServer}
- * implementation that contains exactly one MBean, which is specified at the
- * time of creation.</p>
- *
- * <pre>
- * public class SingletonMBeanServer extends MBeanServerSupport {
- *     private final ObjectName objectName;
- *     private final DynamicMBean mbean;
- *
- *     public SingletonMBeanServer(ObjectName objectName, DynamicMBean mbean) {
- *         this.objectName = objectName;
- *         this.mbean = mbean;
- *     }
- *
- *     &#64;Override
- *     protected {@code Set<ObjectName>} {@link #getNames getNames}() {
- *         return Collections.singleton(objectName);
- *     }
- *
- *     &#64;Override
- *     public DynamicMBean {@link #getDynamicMBeanFor
- *                                getDynamicMBeanFor}(ObjectName name)
- *             throws InstanceNotFoundException {
- *         if (objectName.equals(name))
- *             return mbean;
- *         else
- *             throw new InstanceNotFoundException(name);
- *     }
- * }
- * </pre>
- *
- * <p>Using this class, you could make an {@code MBeanServer} that contains
- * a {@link javax.management.timer.Timer Timer} MBean like this:</p>
- *
- * <pre>
- *     Timer timer = new Timer();
- *     DynamicMBean mbean = new {@link javax.management.StandardMBean
- *                                     StandardMBean}(timer, TimerMBean.class);
- *     ObjectName name = new ObjectName("com.example:type=Timer");
- *     MBeanServer timerMBS = new SingletonMBeanServer(name, mbean);
- * </pre>
- *
- * <p>When {@code getDynamicMBeanFor} always returns the same object for the
- * same name, as here, notifications work in the expected way: if the object
- * is a {@link NotificationEmitter} then listeners can be added using
- * {@link MBeanServer#addNotificationListener(ObjectName, NotificationListener,
- * NotificationFilter, Object) MBeanServer.addNotificationListener}.  If
- * {@code getDynamicMBeanFor} does not always return the same object for the
- * same name, more work is needed to make notifications work, as described
- * <a href="#notifs">below</a>.</p>
- *
- * <h4 id="virtual">Namespaces containing Virtual MBeans</h4>
- *
- * <p>Virtual MBeans are MBeans that do not exist as Java objects,
- * except transiently while they are being accessed.  This is useful when
- * there might be very many of them, or when keeping track of their creation
- * and deletion might be expensive or hard.  For example, you might have one
- * MBean per system process.  With an ordinary {@code MBeanServer}, you would
- * have to list the system processes in order to create an MBean object for
- * each one, and you would have to track the arrival and departure of system
- * processes in order to create or delete the corresponding MBeans.  With
- * Virtual MBeans, you only need the MBean for a given process at the exact
- * point where it is referenced with a call such as
- * {@link MBeanServer#getAttribute MBeanServer.getAttribute}.</p>
- *
- * <p>Here is an example of an {@code MBeanServer} implementation that has
- * one MBean for every system property.  The system property {@code "java.home"}
- * is represented by the MBean called {@code
- * com.example:type=Property,name="java.home"}, with an attribute called
- * {@code Value} that is the value of the property.</p>
- *
- * <pre>
- * public interface PropertyMBean {
- *     public String getValue();
- * }
- *
- * <a name="PropsMBS"></a>public class PropsMBS extends MBeanServerSupport {
- *     private static ObjectName newObjectName(String name) {
- *         try {
- *             return new ObjectName(name);
- *         } catch (MalformedObjectNameException e) {
- *             throw new AssertionError(e);
- *         }
- *     }
- *
- *     public static class PropertyImpl implements PropertyMBean {
- *         private final String name;
- *
- *         public PropertyImpl(String name) {
- *             this.name = name;
- *         }
- *
- *         public String getValue() {
- *             return System.getProperty(name);
- *         }
- *     }
- *
- *     &#64;Override
- *     public DynamicMBean {@link #getDynamicMBeanFor
- *                                getDynamicMBeanFor}(ObjectName name)
- *             throws InstanceNotFoundException {
- *
- *         // Check that the name is a legal one for a Property MBean
- *         ObjectName namePattern = newObjectName(
- *                     "com.example:type=Property,name=\"*\"");
- *         if (!namePattern.apply(name))
- *             throw new InstanceNotFoundException(name);
- *
- *         // Extract the name of the property that the MBean corresponds to
- *         String propName = ObjectName.unquote(name.getKeyProperty("name"));
- *         if (System.getProperty(propName) == null)
- *             throw new InstanceNotFoundException(name);
- *
- *         // Construct and return a transient MBean object
- *         PropertyMBean propMBean = new PropertyImpl(propName);
- *         return new StandardMBean(propMBean, PropertyMBean.class, false);
- *     }
- *
- *     &#64;Override
- *     protected {@code Set<ObjectName>} {@link #getNames getNames}() {
- *         {@code Set<ObjectName> names = new TreeSet<ObjectName>();}
- *         Properties props = System.getProperties();
- *         for (String propName : props.stringPropertyNames()) {
- *             ObjectName objectName = newObjectName(
- *                     "com.example:type=Property,name=" +
- *                     ObjectName.quote(propName));
- *             names.add(objectName);
- *         }
- *         return names;
- *     }
- * }
- * </pre>
- *
- * <p id="virtual-notif-example">Because the {@code getDynamicMBeanFor} method
- * returns a different object every time it is called, the default handling
- * of notifications will not work, as explained <a href="#notifs">below</a>.
- * In this case it does not matter, because the object returned by {@code
- * getDynamicMBeanFor} is not a {@code NotificationEmitter}, so {@link
- * MBeanServer#addNotificationListener(ObjectName, NotificationListener,
- * NotificationFilter, Object) MBeanServer.addNotificationListener} will
- * always fail. But if we wanted to extend {@code PropsMBS} so that the MBean
- * for property {@code "foo"} emitted a notification every time that property
- * changed, we would need to do it as shown below. (Because there is no API to
- * be informed when a property changes, this code assumes that some other code
- * calls the {@code propertyChanged} method every time a property changes.)</p>
- *
- * <pre>
- * public class PropsMBS {
- *     ...as <a href="#PropsMBS">above</a>...
- *
- *     private final {@link VirtualEventManager} vem = new VirtualEventManager();
- *
- *     &#64;Override
- *     public NotificationEmitter {@link #getNotificationEmitterFor
- *                                       getNotificationEmitterFor}(
- *             ObjectName name) throws InstanceNotFoundException {
- *         getDynamicMBeanFor(name);  // check that the name is valid
- *         return vem.{@link VirtualEventManager#getNotificationEmitterFor
- *                           getNotificationEmitterFor}(name);
- *     }
- *
- *     public void propertyChanged(String name, String newValue) {
- *         ObjectName objectName = newObjectName(
- *                 "com.example:type=Property,name=" + ObjectName.quote(name));
- *         Notification n = new Notification(
- *                 "com.example.property.changed", objectName, 0L,
- *                 "Property " + name + " changed");
- *         n.setUserData(newValue);
- *         vem.{@link VirtualEventManager#publish publish}(objectName, n);
- *     }
- * }
- * </pre>
- *
- * <h4 id="creation">MBean creation and deletion</h4>
- *
- * <p>MBean creation through {@code MBeanServer.createMBean} is disabled
- * by default. Subclasses which need to support MBean creation
- * through {@code createMBean} need to implement a single method {@link
- * #createMBean(String, ObjectName, ObjectName, Object[], String[],
- * boolean)}.</p>
- *
- * <p>Similarly MBean registration and unregistration through {@code
- * registerMBean} and {@code unregisterMBean} are disabled by default.
- * Subclasses which need to support MBean registration and
- * unregistration will need to implement {@link #registerMBean registerMBean}
- * and {@link #unregisterMBean unregisterMBean}.</p>
- *
- * <h4 id="notifs">Notifications</h4>
- *
- * <p>By default {@link MBeanServer#addNotificationListener(ObjectName,
- * NotificationListener, NotificationFilter, Object) addNotificationListener}
- * is accepted for an MBean <em>{@code name}</em> if {@link #getDynamicMBeanFor
- * getDynamicMBeanFor}<code>(<em>name</em>)</code> returns an object that is a
- * {@link NotificationEmitter}.  That is appropriate if
- * {@code getDynamicMBeanFor}<code>(<em>name</em>)</code> always returns the
- * same object for the same <em>{@code name}</em>.  But with
- * Virtual MBeans, every call to {@code getDynamicMBeanFor} returns a new object,
- * which is discarded as soon as the MBean request has finished.
- * So a listener added to that object would be immediately forgotten.</p>
- *
- * <p>The simplest way for a subclass that defines Virtual MBeans
- * to support notifications is to create a private {@link VirtualEventManager}
- * and override the method {@link
- * #getNotificationEmitterFor getNotificationEmitterFor} as follows:</p>
- *
- * <pre>
- *     private final VirtualEventManager vem = new VirtualEventManager();
- *
- *     &#64;Override
- *     public NotificationEmitter getNotificationEmitterFor(
- *             ObjectName name) throws InstanceNotFoundException {
- *         // Check that the name is a valid Virtual MBean.
- *         // This is the easiest way to do that, but not always the
- *         // most efficient:
- *         getDynamicMBeanFor(name);
- *
- *         // Return an object that supports add/removeNotificationListener
- *         // through the VirtualEventManager.
- *         return vem.getNotificationEmitterFor(name);
- *     }
- * </pre>
- *
- * <p>A notification <em>{@code n}</em> can then be sent from the Virtual MBean
- * called <em>{@code name}</em> by calling {@link VirtualEventManager#publish
- * vem.publish}<code>(<em>name</em>, <em>n</em>)</code>.  See the example
- * <a href="#virtual-notif-example">above</a>.</p>
- *
- * @since Java SE 7
- */
-public abstract class MBeanServerSupport implements MBeanServer {
-
-    /**
-     * A logger for this class.
-     */
-    private static final Logger LOG =
-            Logger.getLogger(MBeanServerSupport.class.getName());
-
-    /**
-     * <p>Make a new {@code MBeanServerSupport} instance.</p>
-     */
-    protected MBeanServerSupport() {
-    }
-
-    /**
-     * <p>Returns a dynamically created handle that makes it possible to
-     * access the named MBean for the duration of a method call.</p>
-     *
-     * <p>An easy way to create such a {@link DynamicMBean} handle is, for
-     * instance, to create a temporary MXBean instance and to wrap it in
-     * an instance of
-     * {@link javax.management.StandardMBean}.
-     * This handle should remain valid for the duration of the call
-     * but can then be discarded.</p>
-     * @param name the name of the MBean for which a request was received.
-     * @return a {@link DynamicMBean} handle that can be used to invoke
-     * operations on the named MBean.
-     * @throws InstanceNotFoundException if no such MBean is supposed
-     *         to exist.
-     */
-    public abstract DynamicMBean getDynamicMBeanFor(ObjectName name)
-                        throws InstanceNotFoundException;
-
-    /**
-     * <p>Subclasses should implement this method to return
-     * the names of all MBeans handled by this object instance.</p>
-     *
-     * <p>The object returned by getNames() should be safely {@linkplain
-     * Set#iterator iterable} even in the presence of other threads that may
-     * cause the set of names to change. Typically this means one of the
-     * following:</p>
-     *
-     * <ul>
-     * <li>the returned set of names is always the same; or
-     * <li>the returned set of names is an object such as a {@link
-     * java.util.concurrent.CopyOnWriteArraySet CopyOnWriteArraySet} that is
-     * safely iterable even if the set is changed by other threads; or
-     * <li>a new Set is constructed every time this method is called.
-     * </ul>
-     *
-     * @return the names of all MBeans handled by this object.
-     */
-    protected abstract Set<ObjectName> getNames();
-
-    /**
-     * <p>List names matching the given pattern.
-     * The default implementation of this method calls {@link #getNames()}
-     * and returns the subset of those names matching {@code pattern}.</p>
-     *
-     * @param pattern an ObjectName pattern
-     * @return the list of MBean names that match the given pattern.
-     */
-    protected Set<ObjectName> getMatchingNames(ObjectName pattern) {
-        return Util.filterMatchingNames(pattern, getNames());
-    }
-
-    /**
-     * <p>Returns a {@link NotificationEmitter} which can be used to
-     * subscribe or unsubscribe for notifications with the named
-     * mbean.</p>
-     *
-     * <p>The default implementation of this method calls {@link
-     * #getDynamicMBeanFor getDynamicMBeanFor(name)} and returns that object
-     * if it is a {@code NotificationEmitter}, otherwise null. See <a
-     * href="#notifs">above</a> for further discussion of notification
-     * handling.</p>
-     *
-     * @param name The name of the MBean whose notifications are being
-     * subscribed, or unsuscribed.
-     *
-     * @return A {@link NotificationEmitter} that can be used to subscribe or
-     * unsubscribe for notifications emitted by the named MBean, or {@code
-     * null} if the MBean does not emit notifications and should not be
-     * considered as a {@code NotificationEmitter}.
-     *
-     * @throws InstanceNotFoundException if {@code name} is not the name of
-     * an MBean in this {@code MBeanServer}.
-     */
-    public NotificationEmitter getNotificationEmitterFor(ObjectName name)
-            throws InstanceNotFoundException {
-        DynamicMBean mbean = getDynamicMBeanFor(name);
-        if (mbean instanceof NotificationEmitter)
-            return (NotificationEmitter) mbean;
-        else
-            return null;
-    }
-
-    private NotificationEmitter getNonNullNotificationEmitterFor(
-            ObjectName name)
-            throws InstanceNotFoundException {
-        NotificationEmitter emitter = getNotificationEmitterFor(name);
-        if (emitter == null) {
-            IllegalArgumentException iae = new IllegalArgumentException(
-                    "Not a NotificationEmitter: " + name);
-            throw new RuntimeOperationsException(iae);
-        }
-        return emitter;
-    }
-
-    /**
-     * <p>Creates a new MBean in the MBean name space.
-     * This operation is not supported in this base class implementation.</p>
-     * The default implementation of this method always throws an {@link
-     * UnsupportedOperationException}
-     * wrapped in a {@link RuntimeOperationsException}.</p>
-     *
-     * <p>Subclasses may redefine this method to provide an implementation.
-     * All the various flavors of {@code MBeanServer.createMBean} methods
-     * will eventually call this method. A subclass that wishes to
-     * support MBean creation through {@code createMBean} thus only
-     * needs to provide an implementation for this one method.
-     *
-     * @param className The class name of the MBean to be instantiated.
-     * @param name The object name of the MBean. May be null.
-     * @param params An array containing the parameters of the
-     * constructor to be invoked.
-     * @param signature An array containing the signature of the
-     * constructor to be invoked.
-     * @param loaderName The object name of the class loader to be used.
-     * @param useCLR This parameter is {@code true} when this method
-     *        is called from one of the {@code MBeanServer.createMBean} methods
-     *        whose signature does not include the {@code ObjectName} of an
-     *        MBean class loader to use for loading the MBean class.
-     *
-     * @return An <CODE>ObjectInstance</CODE>, containing the
-     * <CODE>ObjectName</CODE> and the Java class name of the newly
-     * instantiated MBean.  If the contained <code>ObjectName</code>
-     * is <code>n</code>, the contained Java class name is
-     * <code>{@link javax.management.MBeanServer#getMBeanInfo
-     * getMBeanInfo(n)}.getClassName()</code>.
-     *
-     * @exception ReflectionException Wraps a
-     * <CODE>java.lang.ClassNotFoundException</CODE> or a
-     * <CODE>java.lang.Exception</CODE> that occurred when trying to
-     * invoke the MBean's constructor.
-     * @exception InstanceAlreadyExistsException The MBean is already
-     * under the control of the MBean server.
-     * @exception MBeanRegistrationException The
-     * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
-     * interface) method of the MBean has thrown an exception. The
-     * MBean will not be registered.
-     * @exception MBeanException The constructor of the MBean has
-     * thrown an exception
-     * @exception NotCompliantMBeanException This class is not a JMX
-     * compliant MBean
-     * @exception InstanceNotFoundException The specified class loader
-     * is not registered in the MBean server.
-     * @exception RuntimeOperationsException Wraps either:
-     * <ul>
-     * <li>a <CODE>java.lang.IllegalArgumentException</CODE>: The className
-     * passed in parameter is null, the <CODE>ObjectName</CODE> passed in
-     * parameter contains a pattern or no <CODE>ObjectName</CODE> is specified
-     * for the MBean; or</li>
-     * <li>an {@code UnsupportedOperationException} if creating MBeans is not
-     * supported by this {@code MBeanServer} implementation.
-     * </ul>
-     */
-    public ObjectInstance createMBean(String className,
-            ObjectName name, ObjectName loaderName, Object[] params,
-            String[] signature, boolean useCLR)
-            throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException, InstanceNotFoundException {
-        throw newUnsupportedException("createMBean");
-    }
-
-
-    /**
-     * <p>Attempts to determine whether the named MBean should be
-     * considered as an instance of a given class.  The default implementation
-     * of this method calls {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
-     * to get an MBean object.  Then its behaviour is the same as the standard
-     * {@link MBeanServer#isInstanceOf MBeanServer.isInstanceOf} method.</p>
-     *
-     * {@inheritDoc}
-     */
-    public boolean isInstanceOf(ObjectName name, String className)
-        throws InstanceNotFoundException {
-
-        final DynamicMBean instance = nonNullMBeanFor(name);
-
-        try {
-            final String mbeanClassName = instance.getMBeanInfo().getClassName();
-
-            if (mbeanClassName.equals(className))
-                return true;
-
-            final Object resource;
-            final ClassLoader cl;
-            if (instance instanceof DynamicWrapperMBean) {
-                DynamicWrapperMBean d = (DynamicWrapperMBean) instance;
-                resource = d.getWrappedObject();
-                cl = d.getWrappedClassLoader();
-            } else {
-                resource = instance;
-                cl = instance.getClass().getClassLoader();
-            }
-
-            final Class<?> classNameClass = Class.forName(className, false, cl);
-
-            if (classNameClass.isInstance(resource))
-                return true;
-
-            if (classNameClass == NotificationBroadcaster.class ||
-                    classNameClass == NotificationEmitter.class) {
-                try {
-                    getNotificationEmitterFor(name);
-                    return true;
-                } catch (Exception x) {
-                    LOG.finest("MBean " + name +
-                            " is not a notification emitter. Ignoring: "+x);
-                    return false;
-                }
-            }
-
-            final Class<?> resourceClass = Class.forName(mbeanClassName, false, cl);
-            return classNameClass.isAssignableFrom(resourceClass);
-        } catch (Exception x) {
-            /* Could be SecurityException or ClassNotFoundException */
-            LOG.logp(Level.FINEST,
-                    MBeanServerSupport.class.getName(),
-                    "isInstanceOf", "Exception calling isInstanceOf", x);
-            return false;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method returns the string
-     * "DefaultDomain".</p>
-     */
-    public String getDefaultDomain() {
-        return "DefaultDomain";
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method returns
-     * {@link #getNames()}.size().</p>
-     */
-    public Integer getMBeanCount() {
-        return getNames().size();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method first calls {@link #getNames
-     * getNames()} to get a list of all MBean names,
-     * and from this set of names, derives the set of domains which contain
-     * MBeans.</p>
-     */
-    public String[] getDomains() {
-        final Set<ObjectName> names = getNames();
-        final Set<String> res = new TreeSet<String>();
-        for (ObjectName n : names) {
-            if (n == null) continue; // not allowed but you never know.
-            res.add(n.getDomain());
-        }
-        return res.toArray(new String[res.size()]);
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link
-     *    #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle
-     * to the named MBean,
-     * and then call {@link DynamicMBean#getAttribute getAttribute}
-     * on that {@link DynamicMBean} handle.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public Object getAttribute(ObjectName name, String attribute)
-        throws MBeanException, AttributeNotFoundException,
-               InstanceNotFoundException, ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        return mbean.getAttribute(attribute);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
-     * to obtain a handle to the named MBean,
-     * and then call {@link DynamicMBean#setAttribute setAttribute}
-     * on that {@link DynamicMBean} handle.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public void setAttribute(ObjectName name, Attribute attribute)
-        throws InstanceNotFoundException, AttributeNotFoundException,
-            InvalidAttributeValueException, MBeanException,
-            ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        mbean.setAttribute(attribute);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
-     * handle to the named MBean,
-     * and then call {@link DynamicMBean#getAttributes getAttributes}
-     * on that {@link DynamicMBean} handle.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public AttributeList getAttributes(ObjectName name,
-            String[] attributes) throws InstanceNotFoundException,
-            ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        return mbean.getAttributes(attributes);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
-     * handle to the named MBean,
-     * and then call {@link DynamicMBean#setAttributes setAttributes}
-     * on that {@link DynamicMBean} handle.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public AttributeList setAttributes(ObjectName name, AttributeList attributes)
-        throws InstanceNotFoundException, ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        return mbean.setAttributes(attributes);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
-     * handle to the named MBean,
-     * and then call {@link DynamicMBean#invoke invoke}
-     * on that {@link DynamicMBean} handle.</p>
-     */
-    public Object invoke(ObjectName name, String operationName,
-                Object[] params, String[] signature)
-                throws InstanceNotFoundException, MBeanException,
-                       ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        return mbean.invoke(operationName, params, signature);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
-     * handle to the named MBean,
-     * and then call {@link DynamicMBean#getMBeanInfo getMBeanInfo}
-     * on that {@link DynamicMBean} handle.</p>
-     */
-    public MBeanInfo getMBeanInfo(ObjectName name)
-        throws InstanceNotFoundException, IntrospectionException,
-               ReflectionException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        return mbean.getMBeanInfo();
-   }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will call
-     * {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}.<!--
-     * -->{@link DynamicMBean#getMBeanInfo getMBeanInfo()}.<!--
-     * -->{@link MBeanInfo#getClassName getClassName()} to get the
-     * class name to combine with {@code name} to produce a new
-     * {@code ObjectInstance}.</p>
-     */
-    public ObjectInstance getObjectInstance(ObjectName name)
-            throws InstanceNotFoundException {
-        final DynamicMBean mbean = nonNullMBeanFor(name);
-        final String className = mbean.getMBeanInfo().getClassName();
-        return new ObjectInstance(name, className);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first call {@link
-     * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle to the
-     * named MBean. If {@code getDynamicMBeanFor} returns an object, {@code
-     * isRegistered} will return true. If {@code getDynamicMBeanFor} returns
-     * null or throws {@link InstanceNotFoundException}, {@code isRegistered}
-     * will return false.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public boolean isRegistered(ObjectName name) {
-        try {
-            final DynamicMBean mbean = getDynamicMBeanFor(name);
-            return mbean!=null;
-        } catch (InstanceNotFoundException x) {
-            if (LOG.isLoggable(Level.FINEST))
-                LOG.finest("MBean "+name+" is not registered: "+x);
-            return false;
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method will first
-     * call {@link #queryNames queryNames}
-     * to get a list of all matching MBeans, and then, for each returned name,
-     * call {@link #getObjectInstance getObjectInstance(name)}.</p>
-     */
-    public Set<ObjectInstance> queryMBeans(ObjectName pattern, QueryExp query) {
-        final Set<ObjectName> names = queryNames(pattern, query);
-        if (names.isEmpty()) return Collections.emptySet();
-        final Set<ObjectInstance> mbeans = new HashSet<ObjectInstance>();
-        for (ObjectName name : names) {
-            try {
-                mbeans.add(getObjectInstance(name));
-            } catch (SecurityException x) { // DLS: OK
-                continue;
-            } catch (InstanceNotFoundException x) { // DLS: OK
-                continue;
-            }
-        }
-        return mbeans;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method calls {@link #getMatchingNames
-     * getMatchingNames(pattern)} to obtain a list of MBeans matching
-     * the given name pattern. If the {@code query} parameter is null,
-     * this will be the result. Otherwise, it will evaluate the
-     * {@code query} parameter for each of the returned names, exactly
-     * as an {@code MBeanServer} would. This might result in
-     * {@link #getDynamicMBeanFor getDynamicMBeanFor} being called
-     * several times for each returned name.</p>
-     */
-    public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
-        try {
-            final Set<ObjectName> res = getMatchingNames(pattern);
-            return filterListOfObjectNames(res, query);
-        } catch (Exception x) {
-            LOG.fine("Unexpected exception raised in queryNames: "+x);
-            LOG.log(Level.FINEST, "Unexpected exception raised in queryNames", x);
-        }
-        // We reach here only when an exception was raised.
-        //
-        return Collections.emptySet();
-    }
-
-    private final static boolean apply(final QueryExp query,
-                  final ObjectName on,
-                  final MBeanServer srv) {
-        boolean res = false;
-        MBeanServer oldServer = QueryEval.getMBeanServer();
-        query.setMBeanServer(srv);
-        try {
-            res = query.apply(on);
-        } catch (Exception e) {
-            LOG.finest("QueryExp.apply threw exception, returning false." +
-                    " Cause: "+e);
-            res = false;
-        } finally {
-           /*
-            * query.setMBeanServer is probably
-            * QueryEval.setMBeanServer so put back the old
-            * value.  Since that method uses a ThreadLocal
-            * variable, this code is only needed for the
-            * unusual case where the user creates a custom
-            * QueryExp that calls a nested query on another
-            * MBeanServer.
-            */
-            query.setMBeanServer(oldServer);
-        }
-        return res;
-    }
-
-    /**
-     * Filters a {@code Set<ObjectName>} according to a pattern and a query.
-     * This might be quite inefficient for virtual name spaces.
-     */
-    Set<ObjectName>
-            filterListOfObjectNames(Set<ObjectName> list,
-                                    QueryExp query) {
-        if (list.isEmpty() || query == null)
-            return list;
-
-        // create a new result set
-        final Set<ObjectName> result = new HashSet<ObjectName>();
-
-        for (ObjectName on : list) {
-            // if on doesn't match query exclude it.
-            if (apply(query, on, this))
-                result.add(on);
-        }
-        return result;
-    }
-
-
-    // Don't use {@inheritDoc}, because we don't want to say that the
-    // MBeanServer replaces a reference to the MBean by its ObjectName.
-    /**
-     * <p>Adds a listener to a registered MBean. A notification emitted by
-     * the MBean will be forwarded to the listener.</p>
-     *
-     * <p>This implementation calls
-     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
-     * and invokes {@code addNotificationListener} on the
-     * {@link NotificationEmitter} it returns.
-     *
-     * @see #getDynamicMBeanFor getDynamicMBeanFor
-     * @see #getNotificationEmitterFor getNotificationEmitterFor
-     */
-    public void addNotificationListener(ObjectName name,
-            NotificationListener listener, NotificationFilter filter,
-            Object handback) throws InstanceNotFoundException {
-        final NotificationEmitter emitter =
-                getNonNullNotificationEmitterFor(name);
-        emitter.addNotificationListener(listener, filter, handback);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation calls
-     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
-     * and invokes {@code removeNotificationListener} on the
-     * {@link NotificationEmitter} it returns.
-     * @see #getDynamicMBeanFor getDynamicMBeanFor
-     * @see #getNotificationEmitterFor getNotificationEmitterFor
-     */
-    public void removeNotificationListener(ObjectName name,
-            NotificationListener listener)
-            throws InstanceNotFoundException, ListenerNotFoundException {
-        final NotificationEmitter emitter =
-                getNonNullNotificationEmitterFor(name);
-        emitter.removeNotificationListener(listener);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation calls
-     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
-     * and invokes {@code removeNotificationListener} on the
-     * {@link NotificationEmitter} it returns.
-     * @see #getDynamicMBeanFor getDynamicMBeanFor
-     * @see #getNotificationEmitterFor getNotificationEmitterFor
-     */
-    public void removeNotificationListener(ObjectName name,
-            NotificationListener listener, NotificationFilter filter,
-            Object handback)
-            throws InstanceNotFoundException, ListenerNotFoundException {
-        NotificationEmitter emitter =
-                getNonNullNotificationEmitterFor(name);
-        emitter.removeNotificationListener(listener);
-    }
-
-
-    /**
-     * <p>Adds a listener to a registered MBean.</p>
-     *
-     * <p>The default implementation of this method first calls
-     * {@link #getDynamicMBeanFor getDynamicMBeanFor(listenerName)}.
-     * If that successfully returns an object, call it {@code
-     * mbean}, then (a) if {@code mbean} is an instance of {@link
-     * NotificationListener} then this method calls {@link
-     * #addNotificationListener(ObjectName, NotificationListener,
-     * NotificationFilter, Object) addNotificationListener(name, mbean, filter,
-     * handback)}, otherwise (b) this method throws an exception as specified
-     * for this case.</p>
-     *
-     * <p>This default implementation is not appropriate for Virtual MBeans,
-     * although that only matters if the object returned by {@code
-     * getDynamicMBeanFor} can be an instance of
-     * {@code NotificationListener}.</p>
-     *
-     * @throws RuntimeOperationsException {@inheritDoc}
-     */
-    public void addNotificationListener(ObjectName name, ObjectName listenerName,
-            NotificationFilter filter, Object handback)
-            throws InstanceNotFoundException {
-        NotificationListener listener = getListenerMBean(listenerName);
-        addNotificationListener(name, listener, filter, handback);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public void removeNotificationListener(ObjectName name,
-            ObjectName listenerName)
-            throws InstanceNotFoundException, ListenerNotFoundException {
-        NotificationListener listener = getListenerMBean(listenerName);
-        removeNotificationListener(name, listener);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public void removeNotificationListener(ObjectName name,
-            ObjectName listenerName, NotificationFilter filter,
-            Object handback)
-            throws InstanceNotFoundException, ListenerNotFoundException {
-        NotificationListener listener = getListenerMBean(listenerName);
-        removeNotificationListener(name, listener, filter, handback);
-    }
-
-    private NotificationListener getListenerMBean(ObjectName listenerName)
-            throws InstanceNotFoundException {
-        Object mbean = getDynamicMBeanFor(listenerName);
-        if (mbean instanceof NotificationListener)
-            return (NotificationListener) mbean;
-        else {
-            throw newIllegalArgumentException(
-                    "MBean is not a NotificationListener: " + listenerName);
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link InstanceNotFoundException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @return the default implementation of this method never returns.
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public ClassLoader getClassLoader(ObjectName loaderName)
-            throws InstanceNotFoundException {
-        final UnsupportedOperationException failed =
-                new UnsupportedOperationException("getClassLoader");
-        final InstanceNotFoundException x =
-                new InstanceNotFoundException(String.valueOf(loaderName));
-        x.initCause(failed);
-        throw x;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method calls
-     * {@link #getDynamicMBeanFor getDynamicMBeanFor(mbeanName)} and applies
-     * the logic just described to the result.</p>
-     */
-    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
-            throws InstanceNotFoundException {
-        final DynamicMBean mbean = nonNullMBeanFor(mbeanName);
-        if (mbean instanceof DynamicWrapperMBean)
-            return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
-        else
-            return mbean.getClass().getClassLoader();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation of this method returns a
-     * {@link ClassLoaderRepository} containing exactly one loader,
-     * the {@linkplain Thread#getContextClassLoader() context class loader}
-     * for the current thread.
-     * Subclasses can override this method to return a different
-     * {@code ClassLoaderRepository}.</p>
-     */
-    public ClassLoaderRepository getClassLoaderRepository() {
-        // We return a new ClassLoaderRepository each time this
-        // method is called. This is by design, because the
-        // SingletonClassLoaderRepository is a very small object and
-        // getClassLoaderRepository() will not be called very often
-        // (the connector server calls it once) - in the context of
-        // MBeanServerSupport there's a very good chance that this method will
-        // *never* be called.
-        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
-        return Util.getSingleClassLoaderRepository(ccl);
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public ObjectInstance registerMBean(Object object, ObjectName name)
-            throws InstanceAlreadyExistsException, MBeanRegistrationException,
-            NotCompliantMBeanException {
-        throw newUnsupportedException("registerMBean");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public void unregisterMBean(ObjectName name)
-            throws InstanceNotFoundException, MBeanRegistrationException {
-        throw newUnsupportedException("unregisterMBean");
-    }
-
-    /**
-     * Calls {@link #createMBean(String, ObjectName,
-     *           ObjectName, Object[], String[], boolean)
-     * createMBean(className, name, null, params, signature, true)};
-     */
-    public final ObjectInstance createMBean(String className, ObjectName name,
-            Object[] params, String[] signature)
-            throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException {
-        try {
-            return safeCreateMBean(className, name, null, params, signature, true);
-        } catch (InstanceNotFoundException ex) {
-            // should not happen!
-            throw new MBeanException(ex, "Unexpected exception: " + ex);
-        }
-    }
-
-    /**
-     * Calls {@link #createMBean(String, ObjectName,
-     *           ObjectName, Object[], String[], boolean)
-     * createMBean(className,name, loaderName, params, signature, false)};
-     */
-    public final ObjectInstance createMBean(String className, ObjectName name,
-            ObjectName loaderName, Object[] params, String[] signature)
-            throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException, InstanceNotFoundException {
-        return safeCreateMBean(className, name, loaderName, params, signature, false);
-    }
-
-    /**
-     * Calls {@link #createMBean(String, ObjectName,
-     *           ObjectName, Object[], String[], boolean)
-     * createMBean(className, name, null, null, null, true)};
-     */
-    public final ObjectInstance createMBean(String className, ObjectName name)
-        throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException {
-        try {
-            return safeCreateMBean(className, name, null, null, null, true);
-        } catch (InstanceNotFoundException ex) {
-            // should not happen!
-            throw new MBeanException(ex, "Unexpected exception: " + ex);
-        }
-    }
-
-    /**
-     * Calls {@link #createMBean(String, ObjectName,
-     *           ObjectName, Object[], String[], boolean)
-     * createMBean(className, name, loaderName, null, null, false)};
-     */
-    public final ObjectInstance createMBean(String className, ObjectName name,
-            ObjectName loaderName)
-            throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException, InstanceNotFoundException {
-        return safeCreateMBean(className, name, loaderName, null, null, false);
-    }
-
-    // make sure all exceptions are correctly wrapped in a JMXException
-    private ObjectInstance safeCreateMBean(String className,
-            ObjectName name, ObjectName loaderName, Object[] params,
-            String[] signature, boolean useRepository)
-            throws ReflectionException, InstanceAlreadyExistsException,
-            MBeanRegistrationException, MBeanException,
-            NotCompliantMBeanException, InstanceNotFoundException {
-        try {
-            return createMBean(className, name, loaderName, params,
-                               signature, useRepository);
-        } catch (ReflectionException x) { throw x;
-        } catch (InstanceAlreadyExistsException x) { throw x;
-        } catch (MBeanRegistrationException x) { throw x;
-        } catch (MBeanException x) { throw x;
-        } catch (NotCompliantMBeanException x) { throw x;
-        } catch (InstanceNotFoundException x) { throw x;
-        } catch (SecurityException x) { throw x;
-        } catch (JMRuntimeException x) { throw x;
-        } catch (RuntimeException x) {
-            throw new RuntimeOperationsException(x, x.toString());
-        } catch (Exception x) {
-            throw new MBeanException(x, x.toString());
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public Object instantiate(String className)
-            throws ReflectionException, MBeanException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public Object instantiate(String className, ObjectName loaderName)
-            throws ReflectionException, MBeanException,
-            InstanceNotFoundException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public Object instantiate(String className, Object[] params,
-            String[] signature) throws ReflectionException, MBeanException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    public Object instantiate(String className, ObjectName loaderName,
-            Object[] params, String[] signature)
-            throws ReflectionException, MBeanException,
-            InstanceNotFoundException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    @Deprecated
-    public ObjectInputStream deserialize(ObjectName name, byte[] data)
-            throws InstanceNotFoundException, OperationsException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    @Deprecated
-    public ObjectInputStream deserialize(String className, byte[] data)
-            throws OperationsException, ReflectionException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This operation is not supported in this base class implementation.
-     * The default implementation of this method always throws
-     * {@link RuntimeOperationsException} wrapping
-     * {@link UnsupportedOperationException}.</p>
-     *
-     * @throws javax.management.RuntimeOperationsException wrapping
-     *        {@link UnsupportedOperationException}
-     */
-    @Deprecated
-    public ObjectInputStream deserialize(String className,
-            ObjectName loaderName, byte[] data)
-            throws InstanceNotFoundException, OperationsException,
-            ReflectionException {
-        throw new UnsupportedOperationException("Not applicable.");
-    }
-
-
-    // Calls getDynamicMBeanFor, and throws an InstanceNotFoundException
-    // if the returned mbean is null.
-    // The DynamicMBean returned by this method is thus guaranteed to be
-    // non null.
-    //
-    private DynamicMBean nonNullMBeanFor(ObjectName name)
-            throws InstanceNotFoundException {
-        if (name == null)
-            throw newIllegalArgumentException("Null ObjectName");
-        if (name.getDomain().equals("")) {
-            String defaultDomain = getDefaultDomain();
-            try {
-                // XXX change to ObjectName.switchDomain
-                // current code DOES NOT PRESERVE the order of keys
-                name = new ObjectName(defaultDomain, name.getKeyPropertyList());
-            } catch (Exception e) {
-                throw newIllegalArgumentException(
-                        "Illegal default domain: " + defaultDomain);
-            }
-        }
-        final DynamicMBean mbean = getDynamicMBeanFor(name);
-        if (mbean!=null) return mbean;
-        throw new InstanceNotFoundException(String.valueOf(name));
-    }
-
-    static RuntimeException newUnsupportedException(String operation) {
-        return new RuntimeOperationsException(
-            new UnsupportedOperationException(
-                operation+": Not supported in this namespace"));
-    }
-
-    static RuntimeException newIllegalArgumentException(String msg) {
-        return new RuntimeOperationsException(
-                new IllegalArgumentException(msg));
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,236 @@
+/*
+ * 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 com.sun.jmx.interceptor;
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.MBeanInstantiator;
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.namespace.NamespaceInterceptor;
+
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXDomain;
+import javax.management.namespace.JMXNamespace;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+/**
+ * A dispatcher that dispatches to NamespaceInterceptors.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class NamespaceDispatchInterceptor
+        extends DispatchInterceptor<NamespaceInterceptor, JMXNamespace> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    private static final int NAMESPACE_SEPARATOR_LENGTH =
+            NAMESPACE_SEPARATOR.length();
+
+    private final DomainDispatchInterceptor localNamespace;
+    private final String           serverName;
+
+    /**
+     * Creates a NamespaceDispatchInterceptor with the specified
+     * repository instance.
+     * <p>Do not forget to call <code>initialize(outer,delegate)</code>
+     * before using this object.
+     *
+     * @param outer A pointer to the MBeanServer object that must be
+     *        passed to the MBeans when invoking their
+     *        {@link javax.management.MBeanRegistration} interface.
+     * @param delegate A pointer to the MBeanServerDelegate associated
+     *        with the new MBeanServer. The new MBeanServer must register
+     *        this MBean in its MBean repository.
+     * @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
+     */
+    public NamespaceDispatchInterceptor(MBeanServer         outer,
+                               MBeanServerDelegate delegate,
+                               MBeanInstantiator   instantiator,
+                               Repository          repository)  {
+           localNamespace = new DomainDispatchInterceptor(outer,delegate,
+                   instantiator,repository,this);
+           serverName = Util.getMBeanServerSecurityName(delegate);
+    }
+
+    // TODO: Should move that to JMXNamespace? or to ObjectName?
+    /**
+     * Get first name space in ObjectName path. Ignore leading namespace
+     * separators.
+     **/
+    public static String getFirstNamespace(ObjectName name) {
+        if (name == null) return "";
+        final String domain = name.getDomain();
+        if (domain.equals("")) return "";
+
+        int first = 0;
+        int end = domain.indexOf(NAMESPACE_SEPARATOR,first);
+        while (end == first) {
+            first = end+NAMESPACE_SEPARATOR_LENGTH;
+            end = domain.indexOf(NAMESPACE_SEPARATOR,first);
+            if (end == -1) break;
+        }
+
+        if (end == -1) return "";
+
+        final String namespace = domain.substring(first,end);
+
+        return namespace;
+    }
+
+    /**
+     * Called by the DefaultMBeanServerInterceptor, just before adding an
+     * MBean to the repository.
+     *
+     * @param resource the MBean to be registered.
+     * @param logicalName the name of the MBean to be registered.
+     */
+    final void checkLocallyRegistrable(Object resource,
+            ObjectName logicalName) {
+        if (!(resource instanceof JMXNamespace) &&
+                logicalName.getDomain().contains(NAMESPACE_SEPARATOR))
+            throw new IllegalArgumentException(String.valueOf(logicalName)+
+                    ": Invalid ObjectName for an instance of " +
+                    resource.getClass().getName());
+    }
+
+    final boolean isLocalHandlerNameFor(String namespace,
+            ObjectName handlerName) {
+        return handlerName.getDomain().equals(namespace+NAMESPACE_SEPARATOR) &&
+               JMXNamespace.TYPE_ASSIGNMENT.equals(
+               handlerName.getKeyPropertyListString());
+    }
+
+    @Override
+    final MBeanServer getInterceptorOrNullFor(ObjectName name) {
+        final String namespace = getFirstNamespace(name);
+        if (namespace.equals("") || isLocalHandlerNameFor(namespace,name) ||
+            name.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
+            LOG.finer("dispatching to local name space");
+            return localNamespace;
+        }
+        final NamespaceInterceptor ns = getInterceptor(namespace);
+        if (LOG.isLoggable(Level.FINER)) {
+            if (ns != null) {
+                LOG.finer("dispatching to name space: " + namespace);
+            } else {
+                LOG.finer("no handler for: " + namespace);
+            }
+        }
+        return ns;
+    }
+
+    @Override
+    final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
+        final String namespace = getFirstNamespace(pattern);
+        if (namespace.equals("") || isLocalHandlerNameFor(namespace,pattern) ||
+            pattern.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
+            LOG.finer("dispatching to local name space");
+            return new QueryInterceptor(localNamespace);
+        }
+        final NamespaceInterceptor ns = getInterceptor(namespace);
+        if (LOG.isLoggable(Level.FINER)) {
+            if (ns != null) {
+                LOG.finer("dispatching to name space: " + namespace);
+            } else {
+                LOG.finer("no handler for: " + namespace);
+            }
+        }
+        if (ns == null) return null;
+        return new QueryInterceptor(ns);
+    }
+
+    @Override
+    final ObjectName getHandlerNameFor(String key)
+        throws MalformedObjectNameException {
+        return ObjectName.getInstance(key+NAMESPACE_SEPARATOR,
+                    "type", JMXNamespace.TYPE);
+    }
+
+    @Override
+    final public String getHandlerKey(ObjectName name) {
+        return getFirstNamespace(name);
+    }
+
+    @Override
+    final NamespaceInterceptor createInterceptorFor(String key,
+            ObjectName name, JMXNamespace handler,
+            Queue<Runnable> postRegisterQueue) {
+        final NamespaceInterceptor ns =
+                new NamespaceInterceptor(serverName,handler,key);
+        if (LOG.isLoggable(Level.FINER)) {
+            LOG.finer("NamespaceInterceptor created: "+ns);
+        }
+        return ns;
+    }
+
+    @Override
+    final DomainDispatchInterceptor getNextInterceptor() {
+        return localNamespace;
+    }
+
+    /**
+     * Returns the list of domains in which any MBean is currently
+     * registered.
+     */
+    @Override
+    public String[] getDomains() {
+        return localNamespace.getDomains();
+    }
+
+    @Override
+    public void addNamespace(ObjectName name, JMXNamespace handler,
+            Queue<Runnable> postRegisterQueue) {
+        if (handler instanceof JMXDomain)
+            localNamespace.addNamespace(name,
+                    (JMXDomain)handler,postRegisterQueue);
+        else super.addNamespace(name,handler,postRegisterQueue);
+    }
+
+    @Override
+    public void removeNamespace(ObjectName name, JMXNamespace handler,
+            Queue<Runnable> postDeregisterQueue) {
+        if (handler instanceof JMXDomain)
+            localNamespace.removeNamespace(name,(JMXDomain)handler,
+                    postDeregisterQueue);
+        else super.removeNamespace(name,handler,postDeregisterQueue);
+    }
+
+
+}
--- a/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java	Thu Sep 04 14:46:36 2008 +0200
@@ -51,6 +51,8 @@
 import javax.management.ObjectName;
 import javax.management.QueryExp;
 import javax.management.ReflectionException;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.MBeanServerSupport;
 import javax.management.remote.IdentityMBeanServerForwarder;
 
 public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
@@ -285,14 +287,14 @@
         if (!pattern.apply(mbeanName))
             return false;
 
-//        final String dompat = pattern.getDomain();
-//        if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
-//            return true; // We already checked that patterns apply.
-//
-//        if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
-//            // only matches if pattern ends with //
-//            return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
-//        }
+        final String dompat = pattern.getDomain();
+        if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
+            return true; // We already checked that patterns apply.
+
+        if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
+            // only matches if pattern ends with //
+            return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
+        }
 
         // should not come here, unless mbeanName contains a // in the
         // middle of its domain, which would be weird.
--- a/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java	Thu Sep 04 14:46:36 2008 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1999-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
@@ -25,44 +25,42 @@
 
 package com.sun.jmx.mbeanserver;
 
-import java.util.Iterator;
-import java.util.logging.Level;
-import java.util.Set;
+import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
+import com.sun.jmx.interceptor.NamespaceDispatchInterceptor;
+
 import java.io.ObjectInputStream;
 import java.security.AccessController;
 import java.security.Permission;
 import java.security.PrivilegedExceptionAction;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.logging.Level;
 
-// RI import
-import javax.management.MBeanPermission;
-import javax.management.AttributeNotFoundException;
-import javax.management.MBeanException;
-import javax.management.ReflectionException;
-import javax.management.MBeanInfo;
-import javax.management.QueryExp;
-import javax.management.NotificationListener;
-import javax.management.NotificationFilter;
-import javax.management.ListenerNotFoundException;
-import javax.management.IntrospectionException;
-import javax.management.OperationsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.NotCompliantMBeanException;
-import javax.management.MBeanRegistrationException;
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InvalidAttributeValueException;
-import javax.management.ObjectName;
-import javax.management.ObjectInstance;
 import javax.management.Attribute;
 import javax.management.AttributeList;
-import javax.management.RuntimeOperationsException;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanPermission;
+import javax.management.MBeanRegistrationException;
 import javax.management.MBeanServer;
 import javax.management.MBeanServerDelegate;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
 import javax.management.loading.ClassLoaderRepository;
 
-import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
-import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
-import com.sun.jmx.interceptor.MBeanServerInterceptor;
-
 /**
  * This is the base class for MBean manipulation on the agent side. It
  * contains the methods necessary for the creation, registration, and
@@ -102,15 +100,14 @@
     /** true if interceptors are enabled **/
     private final boolean interceptorsEnabled;
 
-    /** Revisit: transient ??? **/
-    private final transient MBeanServer outerShell;
+    private final MBeanServer outerShell;
+
+    private volatile MBeanServer mbsInterceptor = null;
 
-    /** Revisit: transient ??? **/
-    private transient MBeanServerInterceptor mbsInterceptor = null;
+    /** The MBeanServerDelegate object representing the MBean Server */
+    private final MBeanServerDelegate mBeanServerDelegateObject;
 
-    /** Revisit: transient ??? **/
-    /** The MBeanServerDelegate object representing the MBean Server */
-    private final transient MBeanServerDelegate mBeanServerDelegateObject;
+    private final String mbeanServerName;
 
     /**
      * <b>Package:</b> Creates an MBeanServer with the
@@ -243,9 +240,10 @@
 
         final Repository repository = new Repository(domain,fairLock);
         this.mbsInterceptor =
-            new DefaultMBeanServerInterceptor(outer, delegate, instantiator,
+            new NamespaceDispatchInterceptor(outer, delegate, instantiator,
                                               repository);
         this.interceptorsEnabled = interceptors;
+        this.mbeanServerName = Util.getMBeanServerSecurityName(delegate);
         initialize();
     }
 
@@ -941,7 +939,8 @@
         throws ReflectionException, MBeanException {
 
         /* Permission check */
-        checkMBeanPermission(className, null, null, "instantiate");
+        checkMBeanPermission(mbeanServerName, className, null, null,
+                "instantiate");
 
         return instantiator.instantiate(className);
     }
@@ -978,7 +977,8 @@
                InstanceNotFoundException {
 
         /* Permission check */
-        checkMBeanPermission(className, null, null, "instantiate");
+        checkMBeanPermission(mbeanServerName, className, null,
+                null, "instantiate");
 
         ClassLoader myLoader = outerShell.getClass().getClassLoader();
         return instantiator.instantiate(className, loaderName, myLoader);
@@ -1016,7 +1016,8 @@
         throws ReflectionException, MBeanException {
 
         /* Permission check */
-        checkMBeanPermission(className, null, null, "instantiate");
+        checkMBeanPermission(mbeanServerName, className, null, null,
+                "instantiate");
 
         ClassLoader myLoader = outerShell.getClass().getClassLoader();
         return instantiator.instantiate(className, params, signature,
@@ -1059,7 +1060,8 @@
                InstanceNotFoundException {
 
         /* Permission check */
-        checkMBeanPermission(className, null, null, "instantiate");
+        checkMBeanPermission(mbeanServerName, className, null,
+                null, "instantiate");
 
         ClassLoader myLoader = outerShell.getClass().getClassLoader();
         return instantiator.instantiate(className,loaderName,params,signature,
@@ -1236,7 +1238,7 @@
                         "Unexpected exception occurred", e);
             }
             throw new
-                IllegalStateException("Can't register delegate.");
+                IllegalStateException("Can't register delegate.",e);
         }
 
 
@@ -1278,7 +1280,7 @@
      *            are not enabled on this object.
      * @see #interceptorsEnabled
      **/
-    public synchronized MBeanServerInterceptor getMBeanServerInterceptor() {
+    public synchronized MBeanServer getMBeanServerInterceptor() {
         if (interceptorsEnabled) return mbsInterceptor;
         else throw new UnsupportedOperationException(
                        "MBeanServerInterceptors are disabled.");
@@ -1292,7 +1294,7 @@
      * @see #interceptorsEnabled
      **/
     public synchronized void
-        setMBeanServerInterceptor(MBeanServerInterceptor interceptor) {
+        setMBeanServerInterceptor(MBeanServer interceptor) {
         if (!interceptorsEnabled) throw new UnsupportedOperationException(
                        "MBeanServerInterceptors are disabled.");
         if (interceptor == null) throw new
@@ -1330,7 +1332,8 @@
      **/
     public ClassLoaderRepository getClassLoaderRepository() {
         /* Permission check */
-        checkMBeanPermission(null, null, null, "getClassLoaderRepository");
+        checkMBeanPermission(mbeanServerName, null, null,
+                null, "getClassLoaderRepository");
         return secureClr;
     }
 
@@ -1484,14 +1487,16 @@
     // SECURITY CHECKS
     //----------------
 
-    private static void checkMBeanPermission(String classname,
+    private static void checkMBeanPermission(String serverName,
+                                             String classname,
                                              String member,
                                              ObjectName objectName,
                                              String actions)
         throws SecurityException {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            Permission perm = new MBeanPermission(classname,
+            Permission perm = new MBeanPermission(serverName,
+                                                  classname,
                                                   member,
                                                   objectName,
                                                   actions);
--- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java	Thu Sep 04 14:46:36 2008 +0200
@@ -224,7 +224,7 @@
         throws InvalidObjectException {
             String domain = prefix + name.getDomain();
             try {
-                name = switchDomain(domain, name);
+                name = name.withDomain(domain);
             } catch (MalformedObjectNameException e) {
                 throw EnvHelp.initCause(
                         new InvalidObjectException(e.getMessage()), e);
@@ -242,7 +242,7 @@
                         "Proxy's name does not start with " + prefix + ": " + name);
             }
             try {
-                name = switchDomain(domain.substring(prefix.length()), name);
+                name = name.withDomain(domain.substring(prefix.length()));
             } catch (MalformedObjectNameException e) {
                 throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e);
             }
@@ -269,14 +269,6 @@
         currentLookup.set(lookup);
     }
 
-    // Method temporarily added until we have ObjectName.switchDomain in the
-    // public API.  Note that this method DOES NOT PRESERVE the order of
-    // keys in the ObjectName so it must not be used in the final release.
-    static ObjectName switchDomain(String domain, ObjectName name)
-            throws MalformedObjectNameException {
-        return new ObjectName(domain, name.getKeyPropertyList());
-    }
-
     private static final ThreadLocal<MXBeanLookup> currentLookup =
             new ThreadLocal<MXBeanLookup>();
 
--- a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java	Thu Sep 04 14:46:36 2008 +0200
@@ -45,7 +45,6 @@
 import javax.management.RuntimeOperationsException;
 
 /**
- * The RepositorySupport implements the Repository interface.
  * This repository does not support persistency.
  *
  * @since 1.5
@@ -197,9 +196,9 @@
                     if (isPropertyValuePattern &&
                         pattern.isPropertyValuePattern(keys[i])) {
                         // wildmatch key property values
-                        final char[] val_pattern = values[i].toCharArray();
-                        final char[] val_string  = v.toCharArray();
-                        if (wildmatch(val_string,val_pattern))
+                        // values[i] is the pattern;
+                        // v is the string
+                        if (Util.wildmatch(v,values[i]))
                             continue;
                         else
                             return false;
@@ -236,86 +235,6 @@
         }
     }
 
-    /** Match a string against a shell-style pattern.  The only pattern
-        characters recognised are <code>?</code>, standing for any one
-        character, and <code>*</code>, standing for any string of
-        characters, including the empty string.
-
-        @param str the string to match, as a character array.
-        @param pat the pattern to match the string against, as a
-        character array.
-
-        @return true if and only if the string matches the pattern.
-    */
-    /* The algorithm is a classical one.  We advance pointers in
-       parallel through str and pat.  If we encounter a star in pat,
-       we remember its position and continue advancing.  If at any
-       stage we get a mismatch between str and pat, we look to see if
-       there is a remembered star.  If not, we fail.  If so, we
-       retreat pat to just past that star and str to the position
-       after the last one we tried, and we let the match advance
-       again.
-
-       Even though there is only one remembered star position, the
-       algorithm works when there are several stars in the pattern.
-       When we encounter the second star, we forget the first one.
-       This is OK, because if we get to the second star in A*B*C
-       (where A etc are arbitrary strings), we have already seen AXB.
-       We're therefore setting up a match of *C against the remainder
-       of the string, which will match if that remainder looks like
-       YC, so the whole string looks like AXBYC.
-    */
-    public static boolean wildmatch(char[] str, char[] pat) {
-        int stri;     // index in str
-        int pati;     // index in pat
-        int starstri; // index for backtrack if "*" attempt fails
-        int starpati; // index for backtrack if "*" attempt fails, +1
-        final int strlen = str.length;
-        final int patlen = pat.length;
-
-        stri = pati = 0;
-        starstri = starpati = -1;
-
-        /* On each pass through this loop, we either advance pati,
-           or we backtrack pati and advance starstri.  Since starstri
-           is only ever assigned from pati, the loop must terminate.  */
-        while (true) {
-            if (pati < patlen) {
-                final char patc = pat[pati];
-                switch (patc) {
-                case '?':
-                    if (stri == strlen)
-                        break;
-                    stri++;
-                    pati++;
-                    continue;
-                case '*':
-                    pati++;
-                    starpati = pati;
-                    starstri = stri;
-                    continue;
-                default:
-                    if (stri < strlen && str[stri] == patc) {
-                        stri++;
-                        pati++;
-                        continue;
-                    }
-                    break;
-                }
-            } else if (stri == strlen)
-                return true;
-
-            // Mismatched, can we backtrack to a "*"?
-            if (starpati < 0 || starstri == strlen)
-                return false;
-
-            // Retry the match one position later in str
-            pati = starpati;
-            starstri++;
-            stri = starstri;
-        }
-    }
-
     private void addNewDomMoi(final DynamicMBean object,
                               final String dom,
                               final ObjectName name,
@@ -370,7 +289,7 @@
         if (name.isPattern()) return null;
 
         // Extract the domain name.
-        String dom= name.getDomain().intern();
+        String dom = name.getDomain().intern();
 
         // Default domain case
         if (dom.length() == 0) {
@@ -480,7 +399,7 @@
             name = Util.newObjectName(domain + name.toString());
 
         // Do we have default domain ?
-        if (dom == domain) {
+        if (dom == domain) {  // ES: OK (dom & domain are interned)
             to_default_domain = true;
             dom = domain;
         } else {
@@ -652,10 +571,9 @@
             }
 
             // Pattern matching in the domain name (*, ?)
-            char[] dom2Match = name.getDomain().toCharArray();
+            final String dom2Match = name.getDomain();
             for (String dom : domainTb.keySet()) {
-                char[] theDom = dom.toCharArray();
-                if (wildmatch(theDom, dom2Match)) {
+                if (Util.wildpathmatch(dom, dom2Match)) {
                     final Map<String,NamedObject> moiTb = domainTb.get(dom);
                     if (allNames)
                         result.addAll(moiTb.values());
@@ -726,7 +644,7 @@
                 // need to reinstantiate a hashtable because of possible
                 // big buckets array size inside table, never cleared,
                 // thus the new !
-                if (dom == domain)
+                if (dom == domain) // ES: OK dom and domain are interned.
                     domainTb.put(domain, new HashMap<String,NamedObject>());
             }
 
--- a/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java	Thu Sep 04 14:46:36 2008 +0200
@@ -28,17 +28,16 @@
 import javax.management.MBeanServer;
 import javax.management.MBeanServerDelegate;
 
-import com.sun.jmx.interceptor.MBeanServerInterceptor;
 
 /**
- * Extends the MBeanServer and MBeanServerInterceptor interface to
+ * Extends the MBeanServer interface to
  * provide methods for getting the MetaData and MBeanServerInstantiator
  * objects associated with an MBeanServer.
  *
  * @since 1.5
  */
 public interface SunJmxMBeanServer
-    extends MBeanServerInterceptor, MBeanServer {
+    extends MBeanServer {
 
     /**
      * Return the MBeanInstantiator associated to this MBeanServer.
@@ -68,7 +67,7 @@
      *            are not enabled on this object.
      * @see #interceptorsEnabled
      **/
-    public MBeanServerInterceptor getMBeanServerInterceptor();
+    public MBeanServer getMBeanServerInterceptor();
 
     /**
      * Set the MBeanServerInterceptor.
@@ -77,7 +76,7 @@
      *            are not enabled on this object.
      * @see #interceptorsEnabled
      **/
-    public void setMBeanServerInterceptor(MBeanServerInterceptor interceptor);
+    public void setMBeanServerInterceptor(MBeanServer interceptor);
 
     /**
      * <p>Return the MBeanServerDelegate representing the MBeanServer.
--- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,8 @@
 
 package com.sun.jmx.mbeanserver;
 
+import com.sun.jmx.defaults.JmxProperties;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,11 +44,22 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.WeakHashMap;
+import java.util.logging.Level;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerFactory;
 import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 import javax.management.loading.ClassLoaderRepository;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
 
 public class Util {
+    private final static int NAMESPACE_SEPARATOR_LENGTH =
+            NAMESPACE_SEPARATOR.length();
+    public final static String ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?";
+
+
     static <K, V> Map<K, V> newMap() {
         return new HashMap<K, V>();
     }
@@ -145,6 +158,270 @@
         return hash;
     }
 
+    /** Match a part of a string against a shell-style pattern.
+        The only pattern characters recognized are <code>?</code>,
+        standing for any one character,
+        and <code>*</code>, standing for any string of
+        characters, including the empty string. For instance,
+        {@code wildmatch("sandwich","sa?d*ch",1,4,1,4)} will match
+        {@code "and"} against {@code "a?d"}.
+
+        @param str  the string containing the sequence to match.
+        @param pat  a string containing a pattern to match the sub string
+                    against.
+        @param stri   the index in the string at which matching should begin.
+        @param strend the index in the string at which the matching should
+                      end.
+        @param pati   the index in the pattern at which matching should begin.
+        @param patend the index in the pattern at which the matching should
+                      end.
+
+        @return true if and only if the string matches the pattern.
+    */
+    /* The algorithm is a classical one.  We advance pointers in
+       parallel through str and pat.  If we encounter a star in pat,
+       we remember its position and continue advancing.  If at any
+       stage we get a mismatch between str and pat, we look to see if
+       there is a remembered star.  If not, we fail.  If so, we
+       retreat pat to just past that star and str to the position
+       after the last one we tried, and we let the match advance
+       again.
+
+       Even though there is only one remembered star position, the
+       algorithm works when there are several stars in the pattern.
+       When we encounter the second star, we forget the first one.
+       This is OK, because if we get to the second star in A*B*C
+       (where A etc are arbitrary strings), we have already seen AXB.
+       We're therefore setting up a match of *C against the remainder
+       of the string, which will match if that remainder looks like
+       YC, so the whole string looks like AXBYC.
+    */
+    private static boolean wildmatch(final String str, final String pat,
+            int stri, final int strend, int pati, final int patend) {
+
+        // System.out.println("matching "+pat.substring(pati,patend)+
+        //        " against "+str.substring(stri, strend));
+        int starstri; // index for backtrack if "*" attempt fails
+        int starpati; // index for backtrack if "*" attempt fails, +1
+
+        starstri = starpati = -1;
+
+        /* On each pass through this loop, we either advance pati,
+           or we backtrack pati and advance starstri.  Since starstri
+           is only ever assigned from pati, the loop must terminate.  */
+        while (true) {
+            if (pati < patend) {
+                final char patc = pat.charAt(pati);
+                switch (patc) {
+                case '?':
+                    if (stri == strend)
+                        break;
+                    stri++;
+                    pati++;
+                    continue;
+                case '*':
+                    pati++;
+                    starpati = pati;
+                    starstri = stri;
+                    continue;
+                default:
+                    if (stri < strend && str.charAt(stri) == patc) {
+                        stri++;
+                        pati++;
+                        continue;
+                    }
+                    break;
+                }
+            } else if (stri == strend)
+                return true;
+
+            // Mismatched, can we backtrack to a "*"?
+            if (starpati < 0 || starstri == strend)
+                return false;
+
+            // Retry the match one position later in str
+            pati = starpati;
+            starstri++;
+            stri = starstri;
+        }
+    }
+
+    /** Match a string against a shell-style pattern.  The only pattern
+        characters recognized are <code>?</code>, standing for any one
+        character, and <code>*</code>, standing for any string of
+        characters, including the empty string.
+
+        @param str the string to match.
+        @param pat the pattern to match the string against.
+
+        @return true if and only if the string matches the pattern.
+    */
+    public static boolean wildmatch(String str, String pat) {
+        return wildmatch(str,pat,0,str.length(),0,pat.length());
+    }
+
+    /**
+     * Matches a string against a pattern, as a name space path.
+     * This is a special matching where * and ?? don't match //.
+     * The string is split in sub-strings separated by //, and the
+     * pattern is split in sub-patterns separated by //. Each sub-string
+     * is matched against its corresponding sub-pattern.
+     * so <elt-1>//<elt2>//...//<elt-n> matches <pat-1>//<pat-2>//...//<pat-q>
+     * only if n==q and for ( i = 1 => n) elt-i matches pat-i.
+     *
+     * In addition, if we encounter a pattern element which is exactly
+     * **, it can match any number of path-elements - but it must match at
+     * least one element.
+     * When we encounter such a meta-wildcard, we remember its position
+     * and the position in the string path, and we advance both the pattern
+     * and the string. Later, if we encounter a mismatch in pattern & string,
+     * we rewind the position in pattern to just after the meta-wildcard,
+     * and we backtrack the string to i+1 element after the position
+     * we had when we first encountered the meta-wildcard, i being the
+     * position when we last backtracked the string.
+     *
+     * The backtracking logic is an adaptation of the logic in wildmatch
+     * above.
+     * See test/javax/mangement/ObjectName/ApplyWildcardTest.java
+     *
+     * Note: this thing is called 'wild' - and that's for a reason ;-)
+     **/
+    public static boolean wildpathmatch(String str, String pat) {
+        final int strlen = str.length();
+        final int patlen = pat.length();
+        int stri = 0;
+        int pati = 0;
+
+        int starstri; // index for backtrack if "**" attempt fails
+        int starpati; // index for backtrack if "**" attempt fails
+
+        starstri = starpati = -1;
+
+        while (true) {
+            // System.out.println("pati="+pati+", stri="+stri);
+            final int strend = str.indexOf(NAMESPACE_SEPARATOR, stri);
+            final int patend = pat.indexOf(NAMESPACE_SEPARATOR, pati);
+
+            // no // remaining in either string or pattern: simple wildmatch
+            // until end of string.
+            if (strend == -1 && patend == -1) {
+                // System.out.println("last sub pattern, last sub element...");
+                // System.out.println("wildmatch("+str.substring(stri,strlen)+
+                //    ","+pat.substring(pati,patlen)+")");
+                return wildmatch(str,pat,stri,strlen,pati,patlen);
+            }
+
+            // no // remaining in string, but at least one remaining in
+            // pattern
+            // => no match
+            if (strend == -1) {
+                // System.out.println("pattern has more // than string...");
+                return false;
+            }
+
+            // strend is != -1, but patend might.
+            // detect wildcard **
+            if (patend == pati+2 && pat.charAt(pati)=='*' &&
+                    pat.charAt(pati+1)=='*') {
+                // if we reach here we know that neither strend nor patend are
+                // equals to -1.
+                stri     = strend + NAMESPACE_SEPARATOR_LENGTH;
+                pati     = patend + NAMESPACE_SEPARATOR_LENGTH;
+                starpati = pati; // position just after **// in pattern
+                starstri = stri; // we eat 1 element in string, and remember
+                                 // the position for backtracking and eating
+                                 // one more element if needed.
+                // System.out.println("starpati="+pati);
+                continue;
+            }
+
+            // This is a bit hacky: * can match // when // is at the end
+            // of the string, so we include the // delimiter in the pattern
+            // matching. Either we're in the middle of the path, so including
+            // // both at the end of the pattern and at the end of the string
+            // has no effect - match(*//,dfsd//) is equivalent to match(*,dfsd)
+            // or we're at the end of the pattern path, in which case
+            // including // at the end of the string will have the desired
+            // effect (provided that we detect the end of matching correctly,
+            // see further on).
+            //
+            final int endpat =
+                    ((patend > -1)?patend+NAMESPACE_SEPARATOR_LENGTH:patlen);
+            final int endstr =
+                    ((strend > -1)?strend+NAMESPACE_SEPARATOR_LENGTH:strlen);
+
+            // if we reach the end of the pattern, or if elt-i & pat-i
+            // don't match, we have a mismatch.
+
+            // Note: we know that strend != -1, therefore patend==-1
+            //       indicates a mismatch unless pattern can match
+            //       a // at the end, and strend+2=strlen.
+            // System.out.println("wildmatch("+str.substring(stri,endstr)+","+
+            //        pat.substring(pati,endpat)+")");
+            if (!wildmatch(str,pat,stri,endstr,pati,endpat)) {
+
+                // System.out.println("nomatch");
+                // if we have a mismatch and didn't encounter any meta-wildcard,
+                // we return false. String & pattern don't match.
+                if (starpati < 0) return false;
+
+                // If we reach here, we had a meta-wildcard.
+                // We need to backtrack to the wildcard, and make it eat an
+                // additional string element.
+                //
+                stri = str.indexOf(NAMESPACE_SEPARATOR, starstri);
+                // System.out.println("eating one additional element? "+stri);
+
+                // If there's no more elements to eat, string and pattern
+                // don't match => return false.
+                if (stri == -1) return false;
+
+                // Backtrack to where we were when we last matched against
+                // the meta-wildcard, make it eat an additional path element,
+                // remember the new positions, and continue from there...
+                //
+                stri = stri + NAMESPACE_SEPARATOR_LENGTH;
+                starstri = stri;
+                pati = starpati;
+                // System.out.println("skiping to stri="+stri);
+                continue;
+            }
+
+            // Here we know that strend > -1 but we can have patend == -1.
+            //
+            // So if we reach here, we know pat-i+//? has matched
+            // elt-i+//
+            //
+            // If patend==-1, we know that there was no delimiter
+            // at the end of the pattern, that we are at the last pattern,
+            // and therefore that pat-i has matched elt-i+//
+            //
+            // In that case we can consider that we have a match only if
+            // elt-i is also the last path element in the string, which is
+            // equivalent to saying that strend+2==strlen.
+            //
+            if (patend == -1 && starpati == -1)
+                return (strend+NAMESPACE_SEPARATOR_LENGTH==strlen);
+
+            // patend != -1, or starpati > -1 so there remains something
+            // to match.
+
+            // go to next pair: elt-(i+1) pat-(i+1);
+            stri = strend + NAMESPACE_SEPARATOR_LENGTH;
+            pati = (patend==-1)?pati:(patend + NAMESPACE_SEPARATOR_LENGTH);
+        }
+    }
+
+    /**
+     * Returns true if the ObjectName's {@code domain} is selected by the
+     * given {@code pattern}.
+     */
+    public static boolean isDomainSelected(String domain, String pattern) {
+        if  (domain == null || pattern == null)
+            throw new IllegalArgumentException("null");
+        return Util.wildpathmatch(domain,pattern);
+    }
+
     /**
      * Filters a set of ObjectName according to a given pattern.
      *
@@ -167,6 +444,34 @@
         return res;
     }
 
+
+    /**
+     * Filters a set of ObjectInstance according to a given pattern.
+     *
+     * @param pattern the pattern that the returned names must match.
+     * @param all     the set of instances to filter.
+     * @return a set of ObjectInstance from which non matching instances
+     *         have been removed.
+     */
+    public static Set<ObjectInstance>
+            filterMatchingInstances(ObjectName pattern,
+                                        Set<ObjectInstance> all) {
+        // If no pattern, just return all names
+        if (pattern == null
+                || all.isEmpty()
+                || ObjectName.WILDCARD.equals(pattern))
+            return all;
+
+        // If there's a pattern, do the matching.
+        final Set<ObjectInstance> res = equivalentEmptySet(all);
+        for (ObjectInstance n : all) {
+            if (n == null) continue;
+            if (pattern.apply(n.getObjectName()))
+                res.add(n);
+        }
+        return res;
+    }
+
     /**
      * An abstract ClassLoaderRepository that contains a single class loader.
      **/
@@ -216,6 +521,160 @@
         return new SingleClassLoaderRepository(loader);
     }
 
+    /**
+     * Returns the name of the given MBeanServer that should be put in a
+     * permission you need.
+     * This corresponds to the
+     * {@code *[;mbeanServerName=<mbeanServerName>[;*]]} property
+     * embedded in the MBeanServerId attribute of the
+     * server's {@link MBeanServerDelegate}.
+     *
+     * @param server The MBean server
+     * @return the name of the MBeanServer, or "*" if the name couldn't be
+     *         obtained, or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}
+     *         if there was no name.
+     */
+    public static String getMBeanServerSecurityName(MBeanServer server) {
+        final String notfound = "*";
+        try {
+            final String mbeanServerId = (String)
+                    server.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
+                    "MBeanServerId");
+            final String found = extractMBeanServerName(mbeanServerId);
+            if (found.length()==0)
+                return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
+            return found;
+        } catch (Exception x) {
+            logshort("Failed to retrieve MBeanServerName for server, " +
+                    "using \"*\"",x);
+            return notfound;
+        }
+    }
+
+    /**
+     * Returns the name of the MBeanServer embedded in the given
+     * mbeanServerId. If the given mbeanServerId doesn't contain any name,
+     * an empty String is returned.
+     * The MBeanServerId is expected to be of the form:
+     * {@code *[;mbeanServerName=<mbeanServerName>[;*]]}
+     * @param mbeanServerId The MBean server ID
+     * @return the name of the MBeanServer if found, or "" if the name was
+     *         not present in the mbeanServerId.
+     */
+    public static String extractMBeanServerName(String mbeanServerId) {
+        if (mbeanServerId==null) return "";
+        final String beginMarker=";mbeanServerName=";
+        final String endMarker=";";
+        final int found = mbeanServerId.indexOf(beginMarker);
+        if (found < 0) return "";
+        final int start = found + beginMarker.length();
+        final int stop = mbeanServerId.indexOf(endMarker, start);
+        return mbeanServerId.substring(start,
+                (stop < 0 ? mbeanServerId.length() : stop));
+    }
+
+    /**
+     * Insert the given mbeanServerName into the given mbeanServerId.
+     * If mbeanServerName is null, empty, or equals to "-", the returned
+     * mbeanServerId will not contain any mbeanServerName.
+     * @param mbeanServerId    The mbeanServerId in which to insert
+     *                         mbeanServerName
+     * @param mbeanServerName  The mbeanServerName
+     * @return an mbeanServerId containing the given mbeanServerName
+     * @throws IllegalArgumentException if mbeanServerId already contains
+     *         a different name, or if the given mbeanServerName is not valid.
+     */
+    public static String insertMBeanServerName(String mbeanServerId,
+            String mbeanServerName) {
+        final String found = extractMBeanServerName(mbeanServerId);
+        if (found.length() > 0 &&
+                found.equals(checkServerName(mbeanServerName)))
+            return mbeanServerId;
+        if (found.length() > 0 && !isMBeanServerNameUndefined(found))
+            throw new IllegalArgumentException(
+                    "MBeanServerName already defined");
+        if (isMBeanServerNameUndefined(mbeanServerName))
+            return mbeanServerId;
+        final String beginMarker=";mbeanServerName=";
+        return mbeanServerId+beginMarker+checkServerName(mbeanServerName);
+    }
+
+    /**
+     * Returns true if the given mbeanServerName corresponds to an
+     * undefined MBeanServerName.
+     * The mbeanServerName is considered undefined if it is one of:
+     * {@code null} or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}.
+     * @param mbeanServerName The mbeanServerName, as returned by
+     *        {@link #extractMBeanServerName(String)}.
+     * @return true if the given name corresponds to one of the forms that
+     *         denotes an undefined MBeanServerName.
+     */
+    public static boolean isMBeanServerNameUndefined(String mbeanServerName) {
+        return mbeanServerName == null ||
+           MBeanServerFactory.DEFAULT_MBEANSERVER_NAME.equals(mbeanServerName);
+    }
+    /**
+     * Check that the provided mbeanServername is syntactically valid.
+     * @param mbeanServerName An mbeanServerName, or {@code null}.
+     * @return mbeanServerName, or {@value
+     * MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if {@code mbeanServerName}
+     * is {@code null}.
+     * @throws IllegalArgumentException if mbeanServerName contains illegal
+     *         characters, or is empty, or is {@code "-"}.
+     *         Illegal characters are {@value #ILLEGAL_MBEANSERVER_NAME_CHARS}.
+     */
+    public static String checkServerName(String mbeanServerName) {
+        if ("".equals(mbeanServerName))
+            throw new IllegalArgumentException(
+                    "\"\" is not a valid MBean server name");
+        if ("-".equals(mbeanServerName))
+            throw new IllegalArgumentException(
+                    "\"-\" is not a valid MBean server name");
+        if (isMBeanServerNameUndefined(mbeanServerName))
+            return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
+        for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS.toCharArray()) {
+            if (mbeanServerName.indexOf(c) >= 0)
+                throw new IllegalArgumentException(
+                        "invalid character in MBeanServer name: "+c);
+        }
+        return mbeanServerName;
+    }
+
+    /**
+     * Get the MBeanServer name that should be put in a permission you need.
+     *
+     * @param delegate The MBeanServerDelegate
+     * @return The MBeanServer name - or {@value
+     * MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if there was no name.
+     */
+    public static String getMBeanServerSecurityName(
+            MBeanServerDelegate delegate) {
+        try {
+            final String serverName = delegate.getMBeanServerName();
+            if (isMBeanServerNameUndefined(serverName))
+                return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
+            return serverName;
+        } catch (Exception x) {
+            logshort("Failed to retrieve MBeanServerName from delegate, " +
+                    "using \"*\"",x);
+            return "*";
+        }
+    }
+
+    // Log the exception and its causes without logging the stack trace.
+    // Use with care - it is usally preferable to log the whole stack trace!
+    // We don't want to log the whole stack trace here: logshort() is
+    // called in those cases where the exception might not be abnormal.
+    private static void logshort(String msg, Throwable t) {
+        if (JmxProperties.MISC_LOGGER.isLoggable(Level.FINE)) {
+            StringBuilder toprint = new StringBuilder(msg);
+               toprint.append("\nCaused By: ").append(String.valueOf(t));
+            while ((t=t.getCause())!=null)
+               toprint.append("\nCaused By: ").append(String.valueOf(t));
+            JmxProperties.MISC_LOGGER.fine(toprint.toString());
+       }
+    }
+
     public static <T> Set<T> cloneSet(Set<T> set) {
         if (set instanceof SortedSet) {
             @SuppressWarnings("unchecked")
@@ -232,10 +691,19 @@
             @SuppressWarnings("unchecked")
             SortedSet<T> sset = (SortedSet<T>) set;
             set = new TreeSet<T>(sset.comparator());
-        } else if (set != null) {
-            set = new HashSet<T>(set.size());
         } else
             set = new HashSet<T>();
         return set;
     }
+
+    // This exception is used when wrapping a class that throws IOException
+    // in a class that doesn't.
+    // The typical example for this are JMXNamespaces, when the sub
+    // MBeanServer can be remote.
+    //
+    public static RuntimeException newRuntimeIOException(IOException io) {
+        final String msg = "Communication failed with underlying resource: "+
+                io.getMessage();
+        return new RuntimeException(msg,io);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,475 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.Util;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.InstanceNotFoundException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanPermission;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.namespace.JMXDomain;
+
+/**
+ * A DomainInterceptor wraps a JMXDomain.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class DomainInterceptor extends HandlerInterceptor<JMXDomain> {
+
+    // TODO: Ideally DomainInterceptor should be replaced by
+    //       something at Repository level.
+    //       The problem there will be that we may need to
+    //       reinstantiate the 'queryPerformedByRepos' boolean
+    //       [or we will need to wrap the repository in
+    //        a 'RepositoryInterceptor'?]
+    //       Also there's no real need for a DomainInterceptor to
+    //       extend RewritingMBeanServerConnection.
+
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    private final String           domainName;
+    private volatile ObjectName    ALL;
+    private final String           serverName;
+    private volatile NotificationListener mbsListener;
+
+    private static class PatternNotificationFilter
+            implements NotificationFilter {
+
+        final ObjectName pattern;
+        public PatternNotificationFilter(ObjectName pattern) {
+            this.pattern = pattern;
+        }
+
+        public boolean isNotificationEnabled(Notification notification) {
+            if (!(notification instanceof MBeanServerNotification))
+                return false;
+            final MBeanServerNotification mbsn =
+                    (MBeanServerNotification) notification;
+            if (pattern == null || pattern.apply(mbsn.getMBeanName()))
+                return true;
+            return false;
+        }
+
+        static final long serialVersionUID = 7409950927025262111L;
+    }
+
+    /**
+     * Creates a new instance of NamespaceInterceptor
+     */
+    public DomainInterceptor(String serverName,
+                             JMXDomain handler,
+                             String domainName) {
+        super(handler);
+        this.domainName = domainName;
+        this.serverName = serverName;
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName()+"(parent="+serverName+
+                ", domain="+this.domainName+")";
+    }
+
+    public void connectDelegate(final MBeanServerDelegate delegate)
+            throws InstanceNotFoundException {
+        final NotificationFilter filter =
+                new PatternNotificationFilter(getPatternFor(null));
+        synchronized (this) {
+            if (mbsListener == null)
+                mbsListener = new NotificationListener() {
+
+               public void handleNotification(Notification notification,
+                    Object handback) {
+                    if (filter.isNotificationEnabled(notification))
+                        delegate.sendNotification(notification);
+                }
+            };
+        }
+
+        getNamespace().
+                addMBeanServerNotificationListener(mbsListener, filter);
+    }
+
+    public void disconnectDelegate()
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        final NotificationListener l;
+        synchronized (this) {
+            l = mbsListener;
+            if (l == null) return;
+            mbsListener = null;
+        }
+        getNamespace().removeMBeanServerNotificationListener(l);
+    }
+
+    public void addPostRegisterTask(Queue<Runnable> queue,
+            final MBeanServerDelegate delegate) {
+        if (queue == null)
+            throw new IllegalArgumentException("task queue must not be null");
+        final Runnable task1 = new Runnable() {
+            public void run() {
+                try {
+                    connectDelegate(delegate);
+                } catch (Exception x) {
+                    throw new UnsupportedOperationException("notification forwarding",x);
+                }
+            }
+        };
+        queue.add(task1);
+    }
+
+    public void addPostDeregisterTask(Queue<Runnable> queue,
+            final MBeanServerDelegate delegate) {
+        if (queue == null)
+            throw new IllegalArgumentException("task queue must not be null");
+        final Runnable task1 = new Runnable() {
+            public void run() {
+                try {
+                    disconnectDelegate();
+                } catch (Exception x) {
+                    throw new UnsupportedOperationException("notification forwarding",x);
+                }
+            }
+        };
+        queue.add(task1);
+    }
+
+    /**
+     * Throws IllegalArgumentException if targetName.getDomain() is not
+     * in the domain handled.
+     **/
+    @Override
+    protected ObjectName toSource(ObjectName targetName) {
+        if (targetName == null) return null;
+        if (targetName.isDomainPattern()) return targetName;
+        final String targetDomain = targetName.getDomain();
+
+        // TODO: revisit this. RuntimeOperationsException may be better?
+        //
+        if (!targetDomain.equals(domainName))
+            throw new IllegalArgumentException(targetName.toString());
+        return targetName;
+    }
+
+    @Override
+    protected ObjectName toTarget(ObjectName sourceName) {
+        return sourceName;
+    }
+
+
+
+    /**
+     * No rewriting: always return sources - stripping instances for which
+     * the caller doesn't have permissions.
+     **/
+    @Override
+    Set<ObjectInstance> processOutputInstances(Set<ObjectInstance> sources) {
+        if (sources == null || sources.isEmpty() || !checkOn())
+            return sources;
+        final Set<ObjectInstance> res = Util.equivalentEmptySet(sources);
+        for (ObjectInstance o : sources) {
+            if (checkQuery(o.getObjectName(), "queryMBeans"))
+                res.add(o);
+        }
+        return res;
+    }
+
+
+    /**
+     * No rewriting: always return sourceNames - stripping names for which
+     * the caller doesn't have permissions.
+     **/
+    @Override
+    Set<ObjectName> processOutputNames(Set<ObjectName> sourceNames) {
+        if (sourceNames == null || sourceNames.isEmpty() || !checkOn())
+            return sourceNames;
+        final Set<ObjectName> res = Util.equivalentEmptySet(sourceNames);
+        for (ObjectName o : sourceNames) {
+            if (checkQuery(o, "queryNames"))
+                res.add(o);
+        }
+        return res;
+    }
+
+    /** No rewriting: always return source **/
+    @Override
+    ObjectInstance processOutputInstance(ObjectInstance source) {
+        return source;
+    }
+
+    @Override
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        try {
+            // We don't trust the wrapped JMXDomain...
+            final ObjectName pattern = getPatternFor(name);
+            final Set<ObjectName> res = super.queryNames(pattern,query);
+            return Util.filterMatchingNames(pattern,res);
+        } catch (Exception x) {
+            if (LOG.isLoggable(Level.FINE))
+                LOG.fine("Unexpected exception raised in queryNames: "+x);
+            LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
+        }
+        // We reach here only when an exception was raised.
+        //
+        final Set<ObjectName> empty = Collections.emptySet();
+        return empty;
+    }
+
+    private ObjectName getPatternFor(final ObjectName name) {
+        try {
+            if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*");
+            if (name == null) return ALL;
+            if (name.getDomain().equals(domainName)) return name;
+            return name.withDomain(domainName);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(String.valueOf(name),x);
+        }
+   }
+
+    @Override
+    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+        try {
+            // We don't trust the wrapped JMXDomain...
+            final ObjectName pattern = getPatternFor(name);
+            final Set<ObjectInstance> res = super.queryMBeans(pattern,query);
+            return Util.filterMatchingInstances(pattern,res);
+        } catch (Exception x) {
+            if (LOG.isLoggable(Level.FINE))
+                LOG.fine("Unexpected exception raised in queryNames: "+x);
+            LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
+        }
+        // We reach here only when an exception was raised.
+        //
+        final Set<ObjectInstance> empty = Collections.emptySet();
+        return empty;
+    }
+
+    @Override
+    public String getDefaultDomain() {
+        return domainName;
+    }
+
+    @Override
+    public String[] getDomains() {
+        return new String[] {domainName};
+    }
+
+    // We call getMBeanCount() on the namespace rather than on the
+    // source server in order to avoid counting MBeans which are not
+    // in the domain.
+    @Override
+    public Integer getMBeanCount() {
+        return getNamespace().getMBeanCount();
+    }
+
+    private boolean checkOn() {
+        final SecurityManager sm = System.getSecurityManager();
+        return (sm != null);
+    }
+
+    //
+    // Implements permission checks.
+    //
+    @Override
+    void check(ObjectName routingName, String member, String action) {
+        if (!checkOn()) return;
+        final String act = (action==null)?"-":action.intern();
+        if(act == "queryMBeans" || act == "queryNames") { // ES: OK
+            // This is tricky. check with 3 parameters is called
+            // by queryNames/queryMBeans before performing the query.
+            // At this point we must check with no class name.
+            // Therefore we pass a className of "-".
+            // The filtering will be done later - processOutputNames and
+            // processOutputInstance will call checkQuery.
+            //
+            check(routingName, "-", "-", act);
+        } else {
+            // This is also tricky:
+            // passing null here will cause check to retrieve the classname,
+            // if needed.
+            check(routingName, null, member, act);
+        }
+    }
+
+    //
+    // Implements permission checks.
+    //
+    @Override
+    void checkCreate(ObjectName routingName, String className, String action) {
+        if (!checkOn()) return;
+        check(routingName,className,"-",action);
+    }
+
+    //
+    // Implements permission checks.
+    //
+    void check(ObjectName routingName, String className, String member,
+            String action) {
+        if (!checkOn()) return;
+        final MBeanPermission perm;
+
+        // action is most probably already an intern string.
+        // string literals are intern strings.
+        // we create a new intern string for 'action' - just to be on
+        // the safe side...
+        // We intern it in order to be able to use == rather than equals
+        // below, because if we don't, and if action is not one of the
+        // 4 literals below, we would have to do a full string comparison.
+        //
+        final String act = (action==null)?"-":action.intern();
+        if (act == "getDomains") { // ES: OK
+            perm = new  MBeanPermission(serverName,"-",member,
+                    routingName,act);
+        } else {
+            final String clazz =
+                    (className==null)?getClassName(routingName):className;
+            perm = new  MBeanPermission(serverName,clazz,member,
+                    routingName,act);
+        }
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null)
+            sm.checkPermission(perm);
+    }
+
+    String getClassName(ObjectName routingName) {
+        if (routingName == null || routingName.isPattern()) return "-";
+        try {
+            return getNamespace().getSourceServer().
+                    getObjectInstance(routingName).getClassName();
+        } catch (InstanceNotFoundException ex) {
+            LOG.finest("Can't get class name for "+routingName+
+                    ", using \"-\". Cause is: "+ex);
+            return "-";
+        }
+    }
+
+    //
+    // Implements permission filters for attributes...
+    //
+    @Override
+    AttributeList checkAttributes(ObjectName routingName,
+            AttributeList attributes, String action) {
+        if (!checkOn()) return attributes;
+        final String className = getClassName(routingName);
+        check(routingName,className,"-",action);
+        if (attributes == null || attributes.isEmpty()) return attributes;
+        final AttributeList res = new AttributeList();
+        for (Attribute at : attributes.asList()) {
+            try {
+                check(routingName,className,at.getName(),action);
+                res.add(at);
+            } catch (SecurityException x) { // DLS: OK
+                continue;
+            }
+        }
+        return res;
+    }
+
+    //
+    // Implements permission filters for attributes...
+    //
+    @Override
+    String[] checkAttributes(ObjectName routingName, String[] attributes,
+            String action) {
+        if (!checkOn()) return attributes;
+        final String className = getClassName(routingName);
+        check(routingName,className,"-",action);
+        if (attributes == null || attributes.length==0) return attributes;
+        final List<String> res = new ArrayList<String>(attributes.length);
+        for (String at : attributes) {
+            try {
+                check(routingName,className,at,action);
+                res.add(at);
+            } catch (SecurityException x) { // DLS: OK
+                continue;
+            }
+        }
+        return res.toArray(new String[res.size()]);
+    }
+
+    //
+    // Implements permission filters for domains...
+    //
+    @Override
+    String[] checkDomains(String[] domains, String action) {
+         if (domains == null || domains.length==0 || !checkOn())
+             return domains;
+         int count=0;
+         for (int i=0;i<domains.length;i++) {
+             try {
+                 check(Util.newObjectName(domains[i]+":x=x"),"-",
+                         "-","getDomains");
+             } catch (SecurityException x) { // DLS: OK
+                 count++;
+                 domains[i]=null;
+             }
+         }
+         if (count == 0) return domains;
+         final String[] res = new String[domains.length-count];
+         count = 0;
+         for (int i=0;i<domains.length;i++)
+             if (domains[i]!=null) res[count++]=domains[i];
+         return res;
+    }
+
+    //
+    // Implements permission filters for queries...
+    //
+    @Override
+    boolean checkQuery(ObjectName routingName, String action) {
+        try {
+            final String className = getClassName(routingName);
+            check(routingName,className,"-",action);
+            return true;
+        } catch (SecurityException x) { // DLS: OK
+            return false;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,577 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.interceptor.MBeanServerInterceptor;
+
+import com.sun.jmx.mbeanserver.Util;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.loading.ClassLoaderRepository;
+import javax.management.namespace.JMXNamespace;
+
+/**
+ * This interceptor wraps a JMXNamespace, and performs
+ * {@code ObjectName} rewriting. {@code HandlerInterceptor} are
+ * usually created and managed by a {@link NamespaceDispatcher} or
+ * {@link DomainDispatcher}.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public abstract class HandlerInterceptor<T extends JMXNamespace>
+        extends RoutingMBeanServerConnection<MBeanServer>
+        implements MBeanServerInterceptor {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    // The wrapped JMXNamespace
+    private final T handler;
+
+    /**
+     * Creates a new instance of HandlerInterceptor
+     */
+    public HandlerInterceptor(T handler) {
+        if (handler == null) throw new IllegalArgumentException("null");
+        this.handler = handler;
+    }
+
+    @Override
+    protected MBeanServer source() {
+         return handler.getSourceServer();
+    }
+
+    // The MBeanServer on which getClassLoader / getClassLoaderFor
+    // will be called.
+    // The NamespaceInterceptor overrides this method - so that it
+    // getClassLoader / getClassLoaderFor don't trigger the loop
+    // detection mechanism.
+    //
+    MBeanServer getServerForLoading() {
+         return source();
+    }
+
+    T getNamespace() {
+        return handler;
+    }
+
+    // If the underlying JMXNamespace throws an IO, the IO will be
+    // wrapped in a RuntimeOperationsException.
+    RuntimeException handleIOException(IOException x,String fromMethodName,
+            Object... params) {
+            // Must do something here?
+        if (LOG.isLoggable(Level.FINEST)) {
+            LOG.finest("IO Exception in "+fromMethodName+": "+x+
+                    " - "+" rethrowing as RuntimeOperationsException.");
+        }
+        throw new RuntimeOperationsException(
+                    Util.newRuntimeIOException(x));
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public AttributeList getAttributes(ObjectName name, String[] attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        try {
+            return super.getAttributes(name, attributes);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getAttributes",name,attributes);
+        }
+    }
+
+    // From MBeanServer
+    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+        throws InstanceNotFoundException {
+        final ObjectName sourceName = toSourceOrRuntime(mbeanName);
+        try {
+            check(mbeanName,null,"getClassLoaderFor");
+            return getServerForLoading().getClassLoaderFor(sourceName);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+
+    // From MBeanServer
+    public ClassLoader getClassLoader(ObjectName loaderName)
+        throws InstanceNotFoundException {
+        final ObjectName sourceName = toSourceOrRuntime(loaderName);
+        try {
+            check(loaderName,null,"getClassLoader");
+            return getServerForLoading().getClassLoader(sourceName);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // From MBeanServer
+    public ObjectInstance registerMBean(Object object, ObjectName name)
+        throws InstanceAlreadyExistsException, MBeanRegistrationException,
+            NotCompliantMBeanException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        try {
+            checkCreate(name,object.getClass().getName(),"registerMBean");
+            return processOutputInstance(
+                    source().registerMBean(object,sourceName));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void removeNotificationListener(ObjectName name, ObjectName listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"removeNotificationListener",name,listener);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public String getDefaultDomain() {
+        try {
+            return super.getDefaultDomain();
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getDefaultDomain");
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public String[] getDomains() {
+        try {
+            return super.getDomains();
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getDomains");
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public Integer getMBeanCount() {
+        try {
+            return super.getMBeanCount();
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getMBeanCount");
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void setAttribute(ObjectName name, Attribute attribute)
+        throws InstanceNotFoundException, AttributeNotFoundException,
+            InvalidAttributeValueException, MBeanException,
+            ReflectionException {
+        try {
+            super.setAttribute(name, attribute);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"setAttribute",name, attribute);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        try {
+            return super.queryNames(name, query);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"queryNames",name, query);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+        try {
+            return super.queryMBeans(name, query);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"queryMBeans",name, query);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public boolean isInstanceOf(ObjectName name, String className)
+        throws InstanceNotFoundException {
+        try {
+            return super.isInstanceOf(name, className);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"isInstanceOf",name, className);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException {
+        try {
+            return super.createMBean(className, name);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"createMBean",className, name);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name,
+                        ObjectName loaderName)
+        throws ReflectionException, InstanceAlreadyExistsException,
+                MBeanRegistrationException, MBeanException,
+                NotCompliantMBeanException, InstanceNotFoundException {
+        try {
+            return super.createMBean(className, name, loaderName);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"createMBean",className, name, loaderName);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public Object getAttribute(ObjectName name, String attribute)
+        throws MBeanException, AttributeNotFoundException,
+            InstanceNotFoundException, ReflectionException {
+        try {
+            return super.getAttribute(name, attribute);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getAttribute",name, attribute);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void removeNotificationListener(ObjectName name, ObjectName listener,
+                            NotificationFilter filter, Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener, filter, handback);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"removeNotificationListener",name,
+                    listener, filter, handback);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                      NotificationListener listener, NotificationFilter filter,
+                      Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener, filter, handback);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"removeNotificationListener",name,
+                    listener, filter, handback);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                NotificationListener listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"removeNotificationListener",name,
+                    listener);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void addNotificationListener(ObjectName name,
+                    NotificationListener listener, NotificationFilter filter,
+                    Object handback) throws InstanceNotFoundException {
+        try {
+            super.addNotificationListener(name, listener, filter, handback);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"addNotificationListener",name,
+                    listener, filter, handback);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void addNotificationListener(ObjectName name, ObjectName listener,
+                NotificationFilter filter, Object handback)
+        throws InstanceNotFoundException {
+        try {
+            super.addNotificationListener(name, listener, filter, handback);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"addNotificationListener",name,
+                    listener, filter, handback);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public boolean isRegistered(ObjectName name) {
+        try {
+            return super.isRegistered(name);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"isRegistered",name);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public void unregisterMBean(ObjectName name)
+        throws InstanceNotFoundException, MBeanRegistrationException {
+        try {
+            super.unregisterMBean(name);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"unregisterMBean",name);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public MBeanInfo getMBeanInfo(ObjectName name)
+        throws InstanceNotFoundException, IntrospectionException,
+            ReflectionException {
+        try {
+            return super.getMBeanInfo(name);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getMBeanInfo",name);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public ObjectInstance getObjectInstance(ObjectName name)
+        throws InstanceNotFoundException {
+        try {
+            return super.getObjectInstance(name);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"getObjectInstance",name);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name,
+                Object[] params, String[] signature)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException {
+        try {
+            return super.createMBean(className, name, params, signature);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"createMBean",className, name,
+                    params, signature);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name,
+                ObjectName loaderName, Object[] params, String[] signature)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException {
+        try {
+            return super.createMBean(className, name, loaderName, params,
+                    signature);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"createMBean",className, name,loaderName,
+                    params, signature);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public AttributeList setAttributes(ObjectName name,AttributeList attributes)
+    throws InstanceNotFoundException, ReflectionException {
+        try {
+            return super.setAttributes(name, attributes);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"setAttributes",name, attributes);
+        }
+    }
+
+    // From MBeanServer: catch & handles IOException
+    @Override
+    public Object invoke(ObjectName name, String operationName, Object[] params,
+                String[] signature)
+        throws InstanceNotFoundException, MBeanException, ReflectionException {
+        try {
+            return super.invoke(name, operationName, params, signature);
+        } catch (IOException ex) {
+            throw handleIOException(ex,"invoke",name, operationName,
+                    params, signature);
+        }
+    }
+
+    //
+    //  These methods are inherited from MBeanServer....
+    //
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className)
+            throws ReflectionException, MBeanException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported instantiate method: " +
+                    "trowing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, ObjectName loaderName)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: instantiate(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, Object[] params,
+            String[] signature) throws ReflectionException, MBeanException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: instantiate(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public Object instantiate(String className, ObjectName loaderName,
+            Object[] params, String[] signature)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: instantiate(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+            throws InstanceNotFoundException, OperationsException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: deserialize(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+            throws OperationsException, ReflectionException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: deserialize(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+            ObjectName loaderName, byte[] data)
+            throws InstanceNotFoundException, OperationsException,
+            ReflectionException {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: deserialize(...) -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * This method should never be called.
+     * Throws UnsupportedOperationException.
+     */
+    public ClassLoaderRepository getClassLoaderRepository() {
+        if (LOG.isLoggable(Level.FINE))
+            LOG.fine("call to unsupported method: getClassLoaderRepository() -" +
+                    "throwing UnsupportedOperationException");
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    static RuntimeException newUnsupportedException(String namespace) {
+        return new RuntimeOperationsException(
+            new UnsupportedOperationException(
+                "Not supported in this namespace: "+namespace));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,369 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanServerConnection;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.event.EventClient;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXAddressable;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+import javax.security.auth.Subject;
+
+/**
+ * A collection of methods that provide JMXConnector wrappers for
+ * JMXRemoteNamepaces underlying connectors.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public final class JMXNamespaceUtils {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+
+    private static <K,V> Map<K,V> newWeakHashMap() {
+        return new WeakHashMap<K,V>();
+    }
+
+    /** Creates a new instance of JMXNamespaces */
+    private JMXNamespaceUtils() {
+    }
+
+    /**
+     * Returns an unmodifiable option map in which the given keys have been
+     * filtered out.
+     * @param keys keys to filter out from the map.
+     * @return An unmodifiable option map in which the given keys have been
+     * filtered out.
+     */
+    public static <K,V> Map<K,V> filterMap(Map<K,V> map, K... keys) {
+        final Map<K,V> filtered;
+        filtered=new HashMap<K,V>(map);
+        for (K key : keys) {
+            filtered.remove(key);
+        }
+        return unmodifiableMap(filtered);
+    }
+
+    // returns un unmodifiable view of a map.
+    public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
+        if (aMap == null || aMap.isEmpty())
+            return Collections.emptyMap();
+        return Collections.unmodifiableMap(aMap);
+    }
+
+
+    /**
+     * A base class that helps writing JMXConnectors that return
+     * MBeanServerConnection wrappers.
+     * This base class wraps an inner JMXConnector (the source), and preserve
+     * its caching policy. If a connection is cached in the source, its wrapper
+     * will be cached in this connector too.
+     * Author's note: rewriting this with java.lang.reflect.Proxy could be
+     * envisaged. It would avoid the combinatory sub-classing introduced by
+     * JMXAddressable.
+     * <p>
+     * Note: all the standard JMXConnector implementations are serializable.
+     *       This implementation here is not. Should it be?
+     *       I believe it must not be serializable unless it becomes
+     *       part of a public API (either standard or officially exposed
+     *       and supported in a documented com.sun package)
+     **/
+     static class JMXCachingConnector
+            implements JMXConnector  {
+
+        // private static final long serialVersionUID = -2279076110599707875L;
+
+        final JMXConnector source;
+
+        // if this object is made serializable, then the variable below
+        // needs to become volatile transient and be lazyly-created...
+        private final
+                Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
+
+
+        public JMXCachingConnector(JMXConnector source) {
+            this.source = checkNonNull(source, "source");
+            connectionMap = newWeakHashMap();
+        }
+
+        private MBeanServerConnection
+                getCached(MBeanServerConnection inner) {
+            return connectionMap.get(inner);
+        }
+
+        private MBeanServerConnection putCached(final MBeanServerConnection inner,
+                final MBeanServerConnection wrapper) {
+            if (inner == wrapper) return wrapper;
+            synchronized (this) {
+                final MBeanServerConnection concurrent =
+                        connectionMap.get(inner);
+                if (concurrent != null) return concurrent;
+                connectionMap.put(inner,wrapper);
+            }
+            return wrapper;
+        }
+
+        public void addConnectionNotificationListener(NotificationListener
+                listener, NotificationFilter filter, Object handback) {
+            source.addConnectionNotificationListener(listener,filter,handback);
+        }
+
+        public void close() throws IOException {
+            source.close();
+        }
+
+        public void connect() throws IOException {
+            source.connect();
+        }
+
+        public void connect(Map<String,?> env) throws IOException {
+            source.connect(env);
+        }
+
+        public String getConnectionId() throws IOException {
+            return source.getConnectionId();
+        }
+
+        /**
+         * Preserve caching policy of the underlying connector.
+         **/
+        public MBeanServerConnection
+                getMBeanServerConnection() throws IOException {
+            final MBeanServerConnection inner =
+                    source.getMBeanServerConnection();
+            final MBeanServerConnection cached = getCached(inner);
+            if (cached != null) return cached;
+            final MBeanServerConnection wrapper = wrap(inner);
+            return putCached(inner,wrapper);
+        }
+
+        public MBeanServerConnection
+                getMBeanServerConnection(Subject delegationSubject)
+                throws IOException {
+            final MBeanServerConnection wrapped =
+                    source.getMBeanServerConnection(delegationSubject);
+            synchronized (this) {
+                final MBeanServerConnection cached = getCached(wrapped);
+                if (cached != null) return cached;
+                final MBeanServerConnection wrapper =
+                    wrapWithSubject(wrapped,delegationSubject);
+                return putCached(wrapped,wrapper);
+            }
+        }
+
+        public void removeConnectionNotificationListener(
+                NotificationListener listener)
+                throws ListenerNotFoundException {
+            source.removeConnectionNotificationListener(listener);
+        }
+
+        public void removeConnectionNotificationListener(
+                NotificationListener l, NotificationFilter f,
+                Object handback) throws ListenerNotFoundException {
+            source.removeConnectionNotificationListener(l,f,handback);
+        }
+
+        /**
+         * This is the method that subclass will redefine. This method
+         * is called by {@code this.getMBeanServerConnection()}.
+         * {@code inner} is the connection returned by
+         * {@code source.getMBeanServerConnection()}.
+         **/
+        protected MBeanServerConnection wrap(MBeanServerConnection inner)
+            throws IOException {
+            return inner;
+        }
+
+        /**
+         * Subclass may also want to redefine this method.
+         * By default it calls wrap(inner). This method
+         * is called by {@code this.getMBeanServerConnection(Subject)}.
+         * {@code inner} is the connection returned by
+         * {@code source.getMBeanServerConnection(Subject)}.
+         **/
+        protected MBeanServerConnection wrapWithSubject(
+                MBeanServerConnection inner, Subject delegationSubject)
+            throws IOException {
+                return wrap(inner);
+        }
+
+        @Override
+        public String toString() {
+            if (source instanceof JMXAddressable) {
+                final JMXServiceURL address =
+                        ((JMXAddressable)source).getAddress();
+                if (address != null)
+                    return address.toString();
+            }
+            return source.toString();
+        }
+
+    }
+
+
+    /**
+     * The name space connector can do 'cd'
+     **/
+    static class JMXNamespaceConnector extends JMXCachingConnector {
+
+        // private static final long serialVersionUID = -4813611540843020867L;
+
+        private final String toDir;
+        private final boolean closeable;
+
+        public JMXNamespaceConnector(JMXConnector source, String toDir,
+                boolean closeable) {
+            super(source);
+            this.toDir = toDir;
+            this.closeable = closeable;
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (!closeable)
+                throw new UnsupportedOperationException("close");
+            else super.close();
+        }
+
+        @Override
+        protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
+               throws IOException {
+            if (LOG.isLoggable(Level.FINER))
+                LOG.finer("Creating name space proxy connection for source: "+
+                        "namespace="+toDir);
+            return JMXNamespaces.narrowToNamespace(wrapped,toDir);
+        }
+
+        @Override
+        public String toString() {
+            return "JMXNamespaces.narrowToNamespace("+
+                    super.toString()+
+                    ", \""+toDir+"\")";
+        }
+
+    }
+
+    static class JMXEventConnector extends JMXCachingConnector {
+
+        // private static final long serialVersionUID = 4742659236340242785L;
+
+        JMXEventConnector(JMXConnector wrapped) {
+            super(wrapped);
+        }
+
+        @Override
+        protected MBeanServerConnection wrap(MBeanServerConnection inner)
+                throws IOException {
+            return EventClient.getEventClientConnection(inner);
+        }
+
+
+        @Override
+        public String toString() {
+            return "EventClient.withEventClient("+super.toString()+")";
+        }
+    }
+
+    static class JMXAddressableEventConnector extends JMXEventConnector
+        implements JMXAddressable {
+
+        // private static final long serialVersionUID = -9128520234812124712L;
+
+        JMXAddressableEventConnector(JMXConnector wrapped) {
+            super(wrapped);
+        }
+
+        public JMXServiceURL getAddress() {
+            return ((JMXAddressable)source).getAddress();
+        }
+    }
+
+    /**
+     * Creates a connector whose MBeamServerConnection will point to the
+     * given sub name space inside the source connector.
+     * @see JMXNamespace
+     **/
+    public static JMXConnector cd(final JMXConnector source,
+                                  final String toNamespace,
+                                  final boolean closeable)
+        throws IOException {
+
+        checkNonNull(source, "JMXConnector");
+
+        if (toNamespace == null || toNamespace.equals(""))
+            return source;
+
+        return new JMXNamespaceConnector(source,toNamespace,closeable);
+    }
+
+
+    /**
+     * Returns a JMX Connector that will use an {@link EventClient}
+     * to subscribe for notifications. If the server doesn't have
+     * an {@link EventClientDelegateMBean}, then the connector will
+     * use the legacy notification mechanism instead.
+     *
+     * @param source The underlying JMX Connector wrapped by the returned
+     *               connector.
+     * @return A JMX Connector that will uses an {@link EventClient}, if
+     *         available.
+     * @see EventClient#getEventClientConnection(MBeanServerConnection)
+     */
+    public static JMXConnector withEventClient(final JMXConnector source) {
+        checkNonNull(source, "JMXConnector");
+        if (source instanceof JMXAddressable)
+            return new JMXAddressableEventConnector(source);
+        else
+            return new JMXEventConnector(source);
+    }
+
+    public static <T> T checkNonNull(T parameter, String name) {
+        if (parameter == null)
+            throw new IllegalArgumentException(name+" must not be null");
+         return parameter;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,449 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespacePermission;
+
+/**
+ * A NamespaceInterceptor wraps a JMXNamespace, performing
+ * ObjectName rewriting.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class NamespaceInterceptor extends HandlerInterceptor<JMXNamespace> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+    private static final Logger PROBE_LOG = Logger.getLogger(
+            JmxProperties.NAMESPACE_LOGGER+".probe");
+
+    // The target name space in which the NamepsaceHandler is mounted.
+    private final String           targetNs;
+
+    private final String           serverName;
+
+    private final ObjectNameRouter proc;
+
+    /**
+     * Internal hack. The JMXRemoteNamespace can be closed and reconnected.
+     * Each time the JMXRemoteNamespace connects, a probe should be sent
+     * to detect cycle. The MBeanServer exposed by JMXRemoteNamespace thus
+     * implements the DynamicProbe interface, which makes it possible for
+     * this handler to know that it should send a new probe.
+     *
+     * XXX: TODO this probe thing is way too complex and fragile.
+     *      This *must* go away or be replaced by something simpler.
+     *      ideas are welcomed.
+     **/
+    public static interface DynamicProbe {
+        public boolean isProbeRequested();
+    }
+
+    /**
+     * Creates a new instance of NamespaceInterceptor
+     */
+    public NamespaceInterceptor(
+            String serverName,
+            JMXNamespace handler,
+            String targetNamespace) {
+        super(handler);
+        this.serverName = serverName;
+        this.targetNs =
+                ObjectNameRouter.normalizeNamespacePath(targetNamespace,
+                true, true, false);
+        proc = new ObjectNameRouter(targetNamespace, "");
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName()+"(parent="+serverName+
+                ", namespace="+this.targetNs+")";
+    }
+
+    /*
+     * XXX: TODO this probe thing is way too complex and fragile.
+     *      This *must* go away or be replaced by something simpler.
+     *      ideas are welcomed.
+     */
+    private volatile boolean probed = false;
+    private volatile ObjectName probe;
+
+    // Query Pattern that we will send through the source server in order
+    // to detect self-linking namespaces.
+    //
+    // XXX: TODO this probe thing is way too complex and fragile.
+    //      This *must* go away or be replaced by something simpler.
+    //      ideas are welcomed.
+    final ObjectName makeProbePattern(ObjectName probe)
+            throws MalformedObjectNameException {
+
+        // we could probably link the probe pattern with the probe - e.g.
+        // using the UUID as key in the pattern - but is it worth it? it
+        // also has some side effects on the context namespace - because
+        // such a probe may get rejected by the jmx.context// namespace.
+        //
+        // The trick here is to devise a pattern that is not likely to
+        // be blocked by intermediate levels. Querying for all namespace
+        // handlers in the source (or source namespace) is more likely to
+        // achieve this goal.
+        //
+        return ObjectName.getInstance("*" +
+                JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
+                JMXNamespace.TYPE_ASSIGNMENT);
+    }
+
+    // tell whether the name pattern corresponds to what might have been
+    // sent as a probe.
+    // XXX: TODO this probe thing is way too complex and fragile.
+    //      This *must* go away or be replaced by something simpler.
+    //      ideas are welcomed.
+    final boolean isProbePattern(ObjectName name) {
+        final ObjectName p = probe;
+        if (p == null) return false;
+        try {
+            return String.valueOf(name).endsWith(targetNs+
+                JMXNamespaces.NAMESPACE_SEPARATOR + "*" +
+                JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
+                JMXNamespace.TYPE_ASSIGNMENT);
+        } catch (RuntimeException x) {
+            // should not happen.
+            PROBE_LOG.finest("Ignoring unexpected exception in self link detection: "+
+                    x);
+            return false;
+        }
+    }
+
+    // The first time a request reaches this NamespaceInterceptor, the
+    // interceptor will send a probe to detect whether the underlying
+    // JMXNamespace links to itslef.
+    //
+    // One way to create such self-linking namespace would be for instance
+    // to create a JMXNamespace whose getSourceServer() method would return:
+    // JMXNamespaces.narrowToNamespace(getMBeanServer(),
+    //                                 getObjectName().getDomain())
+    //
+    // If such an MBeanServer is returned, then any call to that MBeanServer
+    // will trigger an infinite loop.
+    // There can be even trickier configurations if remote connections are
+    // involved.
+    //
+    // In order to prevent this from happening, the NamespaceInterceptor will
+    // send a probe, in an attempt to detect whether it will receive it at
+    // the other end. If the probe is received, an exception will be thrown
+    // in order to break the recursion. The probe is only sent once - when
+    // the first request to the namespace occurs. The DynamicProbe interface
+    // can also be used by a Sun JMXNamespace implementation to request the
+    // emission of a probe at any time (see JMXRemoteNamespace
+    // implementation).
+    //
+    // Probes work this way: the NamespaceInterceptor sets a flag and sends
+    // a queryNames() request. If a queryNames() request comes in when the flag
+    // is on, then it deduces that there is a self-linking loop - and instead
+    // of calling queryNames() on the source MBeanServer of the JMXNamespace
+    // handler (which would cause the loop to go on) it breaks the recursion
+    // by returning the probe ObjectName.
+    // If the NamespaceInterceptor receives the probe ObjectName as result of
+    // its original sendProbe() request it knows that it has been looping
+    // back on itslef and throws an IOException...
+    //
+    //
+    // XXX: TODO this probe thing is way too complex and fragile.
+    //      This *must* go away or be replaced by something simpler.
+    //      ideas are welcomed.
+    //
+    final void sendProbe(MBeanServerConnection msc)
+            throws IOException {
+        try {
+            PROBE_LOG.fine("Sending probe");
+
+            // This is just to prevent any other thread to modify
+            // the probe while the detection cycle is in progress.
+            //
+            final ObjectName probePattern;
+            // we don't want to synchronize on this - we use targetNs
+            // because it's non null and final.
+            synchronized (targetNs) {
+                probed = false;
+                if (probe != null) {
+                    throw new IOException("concurent connection in progress");
+                }
+                final String uuid = UUID.randomUUID().toString();
+                final String endprobe =
+                        JMXNamespaces.NAMESPACE_SEPARATOR + uuid +
+                        ":type=Probe,key="+uuid;
+                final ObjectName newprobe =
+                        ObjectName.getInstance(endprobe);
+                probePattern = makeProbePattern(newprobe);
+                probe = newprobe;
+            }
+
+            try {
+                PROBE_LOG.finer("Probe query: "+probePattern+" expecting: "+probe);
+                final Set<ObjectName> res = msc.queryNames(probePattern, null);
+                final ObjectName expected = probe;
+                PROBE_LOG.finer("Probe res: "+res);
+                if (res.contains(expected)) {
+                    throw new IOException("namespace " +
+                            targetNs + " is linking to itself: " +
+                            "cycle detected by probe");
+                }
+            } catch (SecurityException x) {
+                PROBE_LOG.finer("Can't check for cycles: " + x);
+                // can't do anything....
+            } catch (RuntimeException x) {
+                PROBE_LOG.finer("Exception raised by queryNames: " + x);
+                throw x;
+            } finally {
+                probe = null;
+            }
+        } catch (MalformedObjectNameException x) {
+            final IOException io =
+                    new IOException("invalid name space: probe failed");
+            io.initCause(x);
+            throw io;
+        }
+        PROBE_LOG.fine("Probe returned - no cycles");
+        probed = true;
+    }
+
+    // allows a Sun implementation JMX Namespace, such as the
+    // JMXRemoteNamespace, to control when a probe should be sent.
+    //
+    // XXX: TODO this probe thing is way too complex and fragile.
+    //      This *must* go away or be replaced by something simpler.
+    //      ideas are welcomed.
+    private boolean isProbeRequested(Object o) {
+        if (o instanceof DynamicProbe)
+            return ((DynamicProbe)o).isProbeRequested();
+        return false;
+    }
+
+    /**
+     * This method will send a probe to detect self-linking name spaces.
+     * A self linking namespace is a namespace that links back directly
+     * on itslef. Calling a method on such a name space always results
+     * in an infinite loop going through:
+     * [1]MBeanServer -> [2]NamespaceDispatcher -> [3]NamespaceInterceptor
+     * [4]JMXNamespace -> { network // or cd // or ... } -> [5]MBeanServer
+     * with exactly the same request than [1]...
+     *
+     * The namespace interceptor [2] tries to detect such condition the
+     * *first time* that the connection is used. It does so by setting
+     * a flag, and sending a queryNames() through the name space. If the
+     * queryNames comes back, it knows that there's a loop.
+     *
+     * The DynamicProbe interface can also be used by a Sun JMXNamespace
+     * implementation to request the emission of a probe at any time
+     * (see JMXRemoteNamespace implementation).
+     */
+    private MBeanServer connection() {
+        try {
+            final MBeanServer c = super.source();
+            if (probe != null) // should not happen
+                throw new RuntimeException("connection is being probed");
+
+            if (probed == false || isProbeRequested(c)) {
+                try {
+                    // Should not happen if class well behaved.
+                    // Never probed. Force it.
+                    //System.err.println("sending probe for " +
+                    //        "target="+targetNs+", source="+srcNs);
+                    sendProbe(c);
+                } catch (IOException io) {
+                    throw new RuntimeException(io.getMessage(), io);
+                }
+            }
+
+            if (c != null) {
+                return c;
+            }
+        } catch (RuntimeException x) {
+            throw x;
+        }
+        throw new NullPointerException("getMBeanServerConnection");
+    }
+
+
+    @Override
+    protected MBeanServer source() {
+        return connection();
+    }
+
+    @Override
+    protected MBeanServer getServerForLoading() {
+        // don't want to send probe on getClassLoader/getClassLoaderFor
+        return super.source();
+    }
+
+    /**
+     * Calls {@link MBeanServerConnection#queryNames queryNames}
+     * on the underlying
+     * {@link #getMBeanServerConnection MBeanServerConnection}.
+     **/
+    @Override
+    public final Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        // XXX: TODO this probe thing is way too complex and fragile.
+        //      This *must* go away or be replaced by something simpler.
+        //      ideas are welcomed.
+        PROBE_LOG.finer("probe is: "+probe+" pattern is: "+name);
+        if (probe != null && isProbePattern(name)) {
+            PROBE_LOG.finer("Return probe: "+probe);
+            return Collections.singleton(probe);
+        }
+        return super.queryNames(name, query);
+    }
+
+    @Override
+    protected ObjectName toSource(ObjectName targetName)
+            throws MalformedObjectNameException {
+        return proc.toSourceContext(targetName, true);
+    }
+
+    @Override
+    protected ObjectName toTarget(ObjectName sourceName)
+            throws MalformedObjectNameException {
+        return proc.toTargetContext(sourceName, false);
+    }
+
+    //
+    // Implements permission checks.
+    //
+    @Override
+    void check(ObjectName routingName, String member, String action) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) return;
+        if ("getDomains".equals(action)) return;
+        final JMXNamespacePermission perm =
+                new  JMXNamespacePermission(serverName,member,
+                routingName,action);
+        sm.checkPermission(perm);
+    }
+
+    @Override
+    void checkCreate(ObjectName routingName, String className, String action) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) return;
+        final JMXNamespacePermission perm =
+                new  JMXNamespacePermission(serverName,className,
+                routingName,action);
+        sm.checkPermission(perm);
+    }
+
+    //
+    // Implements permission filters for attributes...
+    //
+    @Override
+    AttributeList checkAttributes(ObjectName routingName,
+            AttributeList attributes, String action) {
+        check(routingName,null,action);
+        if (attributes == null || attributes.isEmpty()) return attributes;
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) return attributes;
+        final AttributeList res = new AttributeList();
+        for (Attribute at : attributes.asList()) {
+            try {
+                check(routingName,at.getName(),action);
+                res.add(at);
+            } catch (SecurityException x) { // DLS: OK
+                continue;
+            }
+        }
+        return res;
+    }
+
+    //
+    // Implements permission filters for attributes...
+    //
+    @Override
+    String[] checkAttributes(ObjectName routingName, String[] attributes,
+            String action) {
+        check(routingName,null,action);
+        if (attributes == null || attributes.length==0) return attributes;
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) return attributes;
+        final List<String> res = new ArrayList<String>(attributes.length);
+        for (String at : attributes) {
+            try {
+                check(routingName,at,action);
+                res.add(at);
+            } catch (SecurityException x) { // DLS: OK
+                continue;
+            }
+        }
+        return res.toArray(new String[res.size()]);
+    }
+
+    //
+    // Implements permission filters for domains...
+    //
+    @Override
+    String[] checkDomains(String[] domains, String action) {
+        // in principle, this method is never called because
+        // getDomains() will never be called - since there's
+        // no way that MBeanServer.getDomains() can be routed
+        // to a NamespaceInterceptor.
+        //
+        // This is also why there's no getDomains() in a
+        // JMXNamespacePermission...
+        //
+        return super.checkDomains(domains, action);
+    }
+
+    //
+    // Implements permission filters for queries...
+    //
+    @Override
+    boolean checkQuery(ObjectName routingName, String action) {
+        try {
+            check(routingName,null,action);
+            return true;
+        } catch (SecurityException x) { // DLS: OK
+            return false;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,191 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * The ObjectNameRouter is used to rewrite routing object names.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class ObjectNameRouter {
+
+    private static final int NAMESPACE_SEPARATOR_LENGTH =
+            NAMESPACE_SEPARATOR.length();
+
+    final String targetPrefix;
+    final String sourcePrefix;
+    final int slen;
+    final int tlen;
+    final boolean identity;
+
+
+    public ObjectNameRouter(String targetDirName) {
+        this(targetDirName,null);
+    }
+
+    /** Creates a new instance of ObjectNameRouter */
+    public ObjectNameRouter(final String remove, final String add) {
+        this.targetPrefix = (remove==null?"":remove);
+        this.sourcePrefix = (add==null?"":add);
+        tlen = targetPrefix.length();
+        slen = sourcePrefix.length();
+        identity = targetPrefix.equals(sourcePrefix);
+    }
+
+    public final ObjectName toTargetContext(ObjectName sourceName,
+            boolean removeLeadingSeparators) {
+        if (sourceName == null) return null;
+        if (identity) return sourceName;
+        String srcDomain = sourceName.getDomain();
+
+        // if the ObjectName starts with // and removeLeadingSeparators is
+        // true, then recursively strip leading //.
+        // Otherwise, do not rewrite ObjectName.
+        //
+        if (srcDomain.startsWith(NAMESPACE_SEPARATOR)) {
+            if (!removeLeadingSeparators) return sourceName;
+            else srcDomain = normalizeDomain(srcDomain,true);
+        }
+        if (slen != 0) {
+            if (!srcDomain.startsWith(sourcePrefix) ||
+                    !srcDomain.startsWith(NAMESPACE_SEPARATOR,slen))
+                throw new IllegalArgumentException(
+                        "ObjectName does not start with expected prefix "
+                        + sourcePrefix + ": " +
+                        String.valueOf(sourceName));
+            srcDomain = srcDomain.substring(slen+NAMESPACE_SEPARATOR_LENGTH);
+        }
+        final String targetDomain =
+                (tlen>0?targetPrefix+NAMESPACE_SEPARATOR+srcDomain:srcDomain);
+        try {
+            return sourceName.withDomain(targetDomain);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(String.valueOf(sourceName),x);
+        }
+    }
+
+    public final ObjectName toSourceContext(ObjectName targetName,
+            boolean removeLeadingSeparators) {
+        if (targetName == null) return null;
+        if (identity) return targetName;
+        String targetDomain = targetName.getDomain();
+        if (targetDomain.startsWith(NAMESPACE_SEPARATOR)) {
+            if (!removeLeadingSeparators) return targetName;
+            else targetDomain =
+                    normalizeDomain(targetDomain,true);
+        }
+        if (tlen != 0) {
+            if (!targetDomain.startsWith(targetPrefix) ||
+                    !targetDomain.startsWith(NAMESPACE_SEPARATOR,tlen))
+                throw new IllegalArgumentException(
+                        "ObjectName does not start with expected prefix "
+                        + targetPrefix + ": " +
+                        String.valueOf(targetName));
+            targetDomain = targetDomain.
+                    substring(tlen+NAMESPACE_SEPARATOR_LENGTH);
+        }
+        final String sourceDomain =
+                (slen>0?sourcePrefix+NAMESPACE_SEPARATOR+targetDomain:
+                    targetDomain);
+        try {
+            return targetName.withDomain(sourceDomain);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(String.valueOf(targetName),x);
+        }
+    }
+
+    public final ObjectInstance toTargetContext(ObjectInstance sourceMoi,
+            boolean removeLeadingSeparators) {
+        if (sourceMoi == null) return null;
+        if (identity) return sourceMoi;
+        return new ObjectInstance(
+                toTargetContext(sourceMoi.getObjectName(),
+                    removeLeadingSeparators),
+                    sourceMoi.getClassName());
+    }
+
+    /**
+     * Removes leading, trailing, or duplicate // in a name space path.
+     **/
+    public static String normalizeDomain(String domain,
+                                         boolean removeLeadingSep) {
+        return normalizeNamespacePath(domain,removeLeadingSep,false,true);
+    }
+
+    /**
+     * Removes leading, trailing, or duplicate // in a name space path.
+     **/
+    public static String normalizeNamespacePath(String namespacePath,
+                                            boolean removeLeadingSep,
+                                            boolean removeTrailingSep,
+                                            boolean endsWithDomain) {
+        if (namespacePath.equals(""))
+            return "";
+        final String[] components = namespacePath.split(NAMESPACE_SEPARATOR);
+        final StringBuilder b =
+                new StringBuilder(namespacePath.length()+NAMESPACE_SEPARATOR_LENGTH);
+        String sep = null;
+        if (!removeLeadingSep && namespacePath.startsWith(NAMESPACE_SEPARATOR))
+            b.append(NAMESPACE_SEPARATOR);
+        int count = 0;
+        for (int i=0; i<components.length; i++) {
+            final String n=components[i];
+            if (n.equals("")) continue;
+            if (n.startsWith("/")||n.endsWith("/")) {
+                // throw exception unless we're looking at the last domain
+                // part of the ObjectName
+                if (! (endsWithDomain && i==(components.length-1))) {
+                    throw new IllegalArgumentException(n+
+                        " is not a valid name space identifier");
+                } else {
+                    // There's a dirty little corner case when the domain
+                    // part (last item) is exactly '/' - in that case we must
+                    // not append '//'
+                    //
+                    removeTrailingSep = removeTrailingSep || n.equals("/");
+                }
+            }
+            if (sep != null) b.append(sep);
+            b.append(n);
+            sep = NAMESPACE_SEPARATOR;
+            count++;
+        }
+        if (!removeTrailingSep && namespacePath.endsWith(NAMESPACE_SEPARATOR)
+            && count > 0)
+            b.append(NAMESPACE_SEPARATOR);
+        return b.toString();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,132 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+
+import com.sun.jmx.defaults.JmxProperties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.MBeanServerConnection;
+import javax.management.namespace.JMXNamespaces;
+
+
+/**
+ * A RoutingConnectionProxy is an MBeanServerConnection proxy that proxies a
+ * source name space in a source MBeanServerConnection.
+ * It wraps a source MBeanServerConnection, and rewrites routing
+ * ObjectNames. It is used to implement
+ * {@code JMXNamespaces.narrowToNamespace(MBeanServerConnection)}.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class RoutingConnectionProxy
+        extends RoutingProxy<MBeanServerConnection> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+
+    /**
+     * Creates a new instance of RoutingConnectionProxy
+     */
+    public RoutingConnectionProxy(MBeanServerConnection source,
+                               String sourceDir) {
+        this(source,sourceDir,"",false);
+    }
+
+    /**
+     * Creates a new instance of RoutingConnectionProxy
+     */
+    public RoutingConnectionProxy(MBeanServerConnection source,
+                               String sourceDir,
+                               String targetDir,
+                               boolean forwardsContext) {
+        super(source,sourceDir,targetDir,forwardsContext);
+
+        if (LOG.isLoggable(Level.FINER))
+            LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
+                      " created");
+    }
+
+    @Override
+    public String toString() {
+        final String targetNs = getTargetNamespace();
+        final String sourceNs = getSourceNamespace();
+        String wrapped = String.valueOf(source());
+        if ("".equals(targetNs)) {
+            if (forwardsContext)
+                wrapped = "ClientContext.withDynamicContext("+wrapped+")";
+            return "JMXNamespaces.narrowToNamespace("+
+                    wrapped+", \""+
+                    sourceNs+"\")";
+        }
+        return this.getClass().getSimpleName()+"("+wrapped+", \""+
+               sourceNs+"\", \""+
+               targetNs+"\", "+forwardsContext+")";
+    }
+
+    public static MBeanServerConnection cd(MBeanServerConnection source,
+            String sourcePath) {
+        if (source == null) throw new IllegalArgumentException("null");
+        if (source.getClass().equals(RoutingConnectionProxy.class)) {
+            // cast is OK here, but findbugs complains unless we use class.cast
+            final RoutingConnectionProxy other =
+                    RoutingConnectionProxy.class.cast(source);
+            final String target = other.getTargetNamespace();
+
+            // Avoid multiple layers of serialization.
+            //
+            // We construct a new proxy from the original source instead of
+            // stacking a new proxy on top of the old one.
+            // - that is we replace
+            //      cd ( cd ( x, dir1), dir2);
+            // by
+            //      cd (x, dir1//dir2);
+            //
+            // We can do this only when the source class is exactly
+            //    NamespaceConnectionProxy.
+            //
+            if (target == null || target.equals("")) {
+                final String path =
+                    JMXNamespaces.concat(other.getSourceNamespace(),
+                    sourcePath);
+                return new RoutingConnectionProxy(other.source(),path,"",
+                        other.forwardsContext);
+            }
+            // Note: we could do possibly something here - but it would involve
+            //       removing part of targetDir, and possibly adding
+            //       something to sourcePath.
+            //       Too complex to bother! => simply default to stacking...
+        }
+        return new RoutingConnectionProxy(source,sourcePath);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,671 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.Util;
+import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+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.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeMBeanException;
+import javax.management.RuntimeOperationsException;
+
+/**
+ * A RoutingMBeanServerConnection wraps a MBeanServerConnection, defining
+ * abstract methods that can be implemented by subclasses to rewrite
+ * routing ObjectNames. It is used to implement
+ * HandlerInterceptors (wrapping JMXNamespace instances) and routing
+ * proxies (used to implement cd operations).
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public abstract class RoutingMBeanServerConnection<T extends MBeanServerConnection>
+        implements MBeanServerConnection {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    /**
+     * Creates a new instance of RoutingMBeanServerConnection
+     */
+    public RoutingMBeanServerConnection() {
+    }
+
+    /**
+     * Returns the wrapped source connection.
+     **/
+    protected abstract T source() throws IOException;
+
+    /**
+     * Converts a target ObjectName to a source ObjectName.
+     **/
+    protected abstract ObjectName toSource(ObjectName targetName)
+        throws MalformedObjectNameException;
+
+    /**
+     * Converts a source ObjectName to a target ObjectName.
+     **/
+    protected abstract ObjectName toTarget(ObjectName sourceName)
+        throws MalformedObjectNameException;
+
+    /**
+     * Can be overridden by subclasses to check the validity of a new
+     * ObjectName used in createMBean or registerMBean.
+     * This method is typically used by subclasses which might require
+     * special handling for "null";
+     **/
+    protected ObjectName newSourceMBeanName(ObjectName targetName)
+        throws MBeanRegistrationException {
+        try {
+            return toSource(targetName);
+        } catch (Exception x) {
+            throw new MBeanRegistrationException(x,"Illegal MBean Name");
+        }
+    }
+
+    // Calls toSource(), Wraps MalformedObjectNameException.
+    ObjectName toSourceOrRuntime(ObjectName targetName)
+        throws RuntimeOperationsException {
+        try {
+            return toSource(targetName);
+        } catch (MalformedObjectNameException x) {
+            final IllegalArgumentException x2 =
+                    new IllegalArgumentException(String.valueOf(targetName),x);
+            final RuntimeOperationsException x3 =
+                    new RuntimeOperationsException(x2);
+            throw x3;
+        }
+    }
+
+
+    // Wraps given exception if needed.
+    RuntimeException makeCompliantRuntimeException(Exception x) {
+        if (x instanceof SecurityException)  return (SecurityException)x;
+        if (x instanceof JMRuntimeException) return (JMRuntimeException)x;
+        if (x instanceof RuntimeException)
+            return new RuntimeOperationsException((RuntimeException)x);
+        if (x instanceof IOException)
+            return Util.newRuntimeIOException((IOException)x);
+        // shouldn't come here...
+        final RuntimeException x2 = new UndeclaredThrowableException(x);
+        return new RuntimeOperationsException(x2);
+    }
+
+    /**
+     * This method is a hook to implement permission checking in subclasses.
+     * By default, this method does nothing and simply returns
+     * {@code attribute}.
+     *
+     * @param routingName The name of the MBean in the enclosing context.
+     *        This is of the form {@code <namespace>//<ObjectName>}.
+     * @param attributes  The list of attributes to check permission for.
+     * @param action one of "getAttribute" or "setAttribute"
+     * @return The list of attributes for which the callers has the
+     *         appropriate {@link
+     *         javax.management.namespace.JMXNamespacePermission}.
+     */
+    String[] checkAttributes(ObjectName routingName,
+            String[] attributes, String action) {
+        check(routingName,null,action);
+        return attributes;
+    }
+
+    /**
+     * This method is a hook to implement permission checking in subclasses.
+     * By default, this method does nothing and simply returns
+     * {@code attribute}.
+     *
+     * @param routingName The name of the MBean in the enclosing context.
+     *        This is of the form {@code <namespace>//<ObjectName>}.
+     * @param attributes The list of attributes to check permission for.
+     * @param action one of "getAttribute" or "setAttribute"
+     * @return The list of attributes for which the callers has the
+     *         appropriate {@link
+     *         javax.management.namespace.JMXNamespacePermission}.
+     */
+    AttributeList checkAttributes(ObjectName routingName,
+            AttributeList attributes, String action) {
+        check(routingName,null,action);
+        return attributes;
+    }
+
+    // from MBeanServerConnection
+    public AttributeList getAttributes(ObjectName name, String[] attributes)
+        throws InstanceNotFoundException, ReflectionException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            final String[] authorized =
+                    checkAttributes(name,attributes,"getAttribute");
+            final AttributeList attrList =
+                    source().getAttributes(sourceName,authorized);
+            return attrList;
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+   /**
+     * This method is a hook to implement permission checking in subclasses.
+     * By default, this method does nothing.
+     * A subclass may override this method and throw a {@link
+     * SecurityException} if the permission is denied.
+     *
+     * @param routingName The name of the MBean in the enclosing context.
+     *        This is of the form {@code <namespace>//<ObjectName>}.
+     * @param member The {@link
+     *  javax.management.namespace.JMXNamespacePermission#getMember member}
+     *  name.
+     * @param action The {@link
+     *  javax.management.namespace.JMXNamespacePermission#getActions action}
+     *  name.
+     */
+    void check(ObjectName routingName,
+               String member, String action) {
+    }
+
+    void checkPattern(ObjectName routingPattern,
+               String member, String action) {
+        // pattern is checked only at posteriori by checkQuery.
+        // checking it a priori usually doesn't work, because ObjectName.apply
+        // does not work between two patterns.
+        check(null,null,action);
+    }
+
+    void checkCreate(ObjectName routingName, String className,
+                     String action) {
+    }
+
+    // from MBeanServerConnection
+    public Object invoke(ObjectName name, String operationName, Object[] params,
+                         String[] signature)
+        throws InstanceNotFoundException, MBeanException, ReflectionException,
+            IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, operationName, "invoke");
+            final Object result =
+                    source().invoke(sourceName,operationName,params,
+                                   signature);
+            return result;
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void unregisterMBean(ObjectName name)
+        throws InstanceNotFoundException, MBeanRegistrationException,
+            IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, null, "unregisterMBean");
+            source().unregisterMBean(sourceName);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public MBeanInfo getMBeanInfo(ObjectName name)
+        throws InstanceNotFoundException, IntrospectionException,
+            ReflectionException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, null, "getMBeanInfo");
+            return source().getMBeanInfo(sourceName);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public ObjectInstance getObjectInstance(ObjectName name)
+        throws InstanceNotFoundException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, null, "getObjectInstance");
+            return processOutputInstance(
+                    source().getObjectInstance(sourceName));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public boolean isRegistered(ObjectName name) throws IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            return source().isRegistered(sourceName);
+        } catch (RuntimeMBeanException x) {
+            throw new RuntimeOperationsException(x.getTargetException());
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void setAttribute(ObjectName name, Attribute attribute)
+        throws InstanceNotFoundException, AttributeNotFoundException,
+            InvalidAttributeValueException, MBeanException,
+            ReflectionException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name,
+                  (attribute==null?null:attribute.getName()),
+                  "setAttribute");
+            source().setAttribute(sourceName,attribute);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public ObjectInstance createMBean(String className,
+            ObjectName name, ObjectName loaderName,
+            Object[] params, String[] signature)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException, IOException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        // Loader Name is already a sourceLoaderName.
+        final ObjectName sourceLoaderName = loaderName;
+        try {
+            checkCreate(name, className, "instantiate");
+            checkCreate(name, className, "registerMBean");
+            final ObjectInstance instance =
+                    source().createMBean(className,sourceName,
+                                         sourceLoaderName,
+                                         params,signature);
+            return processOutputInstance(instance);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public ObjectInstance createMBean(String className, ObjectName name,
+            Object[] params, String[] signature)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, IOException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        try {
+            checkCreate(name, className, "instantiate");
+            checkCreate(name, className, "registerMBean");
+            return processOutputInstance(source().createMBean(className,
+                    sourceName,params,signature));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public ObjectInstance createMBean(String className, ObjectName name,
+            ObjectName loaderName)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException, IOException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        // Loader Name is already a source Loader Name.
+        final ObjectName sourceLoaderName = loaderName;
+        try {
+            checkCreate(name, className, "instantiate");
+            checkCreate(name, className, "registerMBean");
+            return processOutputInstance(source().createMBean(className,
+                    sourceName,sourceLoaderName));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public ObjectInstance createMBean(String className, ObjectName name)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, IOException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        try {
+            checkCreate(name, className, "instantiate");
+            checkCreate(name, className, "registerMBean");
+            return processOutputInstance(source().
+                    createMBean(className,sourceName));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+     }
+
+    // from MBeanServerConnection
+    public Object getAttribute(ObjectName name, String attribute)
+        throws MBeanException, AttributeNotFoundException,
+            InstanceNotFoundException, ReflectionException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, attribute, "getAttribute");
+            return source().getAttribute(sourceName,attribute);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public boolean isInstanceOf(ObjectName name, String className)
+        throws InstanceNotFoundException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name, null, "isInstanceOf");
+            return source().isInstanceOf(sourceName,className);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public AttributeList setAttributes(ObjectName name, AttributeList attributes)
+        throws InstanceNotFoundException, ReflectionException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            final AttributeList authorized =
+                    checkAttributes(name, attributes, "setAttribute");
+            return source().
+                    setAttributes(sourceName,authorized);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // Return names in the target's context.
+    Set<ObjectInstance> processOutputInstances(Set<ObjectInstance> sources) {
+
+        final Set<ObjectInstance> result = Util.equivalentEmptySet(sources);
+        for (ObjectInstance i : sources) {
+            try {
+                final ObjectInstance target = processOutputInstance(i);
+                if (!checkQuery(target.getObjectName(), "queryMBeans"))
+                    continue;
+                result.add(target);
+            } catch (Exception x) {
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.fine("Skiping returned item: " +
+                             "Unexpected exception while processing " +
+                             "ObjectInstance: " + x);
+                }
+                continue;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This is a hook to implement permission checking in subclasses.
+     *
+     * Checks that the caller has sufficient permission for returning
+     * information about {@code sourceName} in {@code action}.
+     *
+     * By default always return true. Subclass may override this method
+     * and return false if the caller doesn't have sufficient permissions.
+     *
+     * @param routingName The name of the MBean to include or exclude from
+     *        the query, expressed in the enclosing context.
+     *        This is of the form {@code <namespace>//<ObjectName>}.
+     * @param action one of "queryNames" or "queryMBeans"
+     * @return true if {@code sourceName} can be returned.
+     */
+    boolean checkQuery(ObjectName routingName, String action) {
+        return true;
+    }
+
+    // Return names in the target's context.
+    ObjectInstance processOutputInstance(ObjectInstance source) {
+        if (source == null) return null;
+        final ObjectName sourceName = source.getObjectName();
+        try {
+            final ObjectName targetName = toTarget(sourceName);
+            return new ObjectInstance(targetName,source.getClassName());
+        } catch (MalformedObjectNameException x) {
+            final IllegalArgumentException x2 =
+                    new IllegalArgumentException(String.valueOf(sourceName),x);
+            final RuntimeOperationsException x3 =
+                    new RuntimeOperationsException(x2);
+            throw x3;
+        }
+    }
+
+    // Returns names in the target's context.
+    Set<ObjectName> processOutputNames(Set<ObjectName> sourceNames) {
+
+        final Set<ObjectName> names = Util.equivalentEmptySet(sourceNames);
+        for (ObjectName n : sourceNames) {
+            try {
+                final ObjectName targetName = toTarget(n);
+                if (!checkQuery(targetName, "queryNames")) continue;
+                names.add(targetName);
+            } catch (Exception x) {
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.fine("Skiping returned item: " +
+                             "Unexpected exception while processing " +
+                             "ObjectInstance: " + x);
+                }
+                continue;
+            }
+        }
+        return names;
+    }
+
+    // from MBeanServerConnection
+    public Set<ObjectInstance> queryMBeans(ObjectName name,
+            QueryExp query) throws IOException {
+        if (name == null) name=ObjectName.WILDCARD;
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            checkPattern(name,null,"queryMBeans");
+            return processOutputInstances(
+                    source().queryMBeans(sourceName,query));
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query)
+        throws IOException {
+        if (name == null) name=ObjectName.WILDCARD;
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            checkPattern(name,null,"queryNames");
+            final Set<ObjectName> tmp = source().queryNames(sourceName,query);
+            final Set<ObjectName> out = processOutputNames(tmp);
+            //System.err.println("queryNames: out: "+out);
+            return out;
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void removeNotificationListener(ObjectName name,
+            NotificationListener listener)
+        throws InstanceNotFoundException,
+        ListenerNotFoundException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name,null,"removeNotificationListener");
+            source().removeNotificationListener(sourceName,listener);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void addNotificationListener(ObjectName name, ObjectName listener,
+            NotificationFilter filter, Object handback)
+            throws InstanceNotFoundException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        // Listener name is already a source listener name.
+        try {
+            check(name,null,"addNotificationListener");
+            source().addNotificationListener(sourceName,listener,
+                    filter,handback);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void addNotificationListener(ObjectName name,
+                NotificationListener listener, NotificationFilter filter,
+                Object handback) throws InstanceNotFoundException, IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name,null,"addNotificationListener");
+            source().addNotificationListener(sourceName, listener, filter,
+                    handback);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+
+    // from MBeanServerConnection
+    public void removeNotificationListener(ObjectName name,
+            NotificationListener listener, NotificationFilter filter,
+            Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException,
+                IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name,null,"removeNotificationListener");
+            source().removeNotificationListener(sourceName,listener,filter,
+                    handback);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void removeNotificationListener(ObjectName name, ObjectName listener,
+            NotificationFilter filter, Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException,
+            IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            check(name,null,"removeNotificationListener");
+            source().removeNotificationListener(sourceName,listener,
+                    filter,handback);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public void removeNotificationListener(ObjectName name, ObjectName listener)
+        throws InstanceNotFoundException, ListenerNotFoundException,
+               IOException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        // listener name is already a source name...
+        final ObjectName sourceListener = listener;
+        try {
+            check(name,null,"removeNotificationListener");
+            source().removeNotificationListener(sourceName,sourceListener);
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public Integer getMBeanCount() throws IOException {
+        try {
+            return source().getMBeanCount();
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // from MBeanServerConnection
+    public String[] getDomains() throws IOException {
+        try {
+            check(null,null,"getDomains");
+            final String[] domains = source().getDomains();
+            return checkDomains(domains,"getDomains");
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    /**
+     * This method is a hook to implement permission checking in subclasses.
+     * Checks that the caller as the necessary permissions to view the
+     * given domain. If not remove the domains for which the caller doesn't
+     * have permission from the list.
+     * <p>
+     * By default, this method always returns {@code domains}
+     *
+     * @param domains The domains to return.
+     * @param action  "getDomains"
+     * @return a filtered list of domains.
+     */
+    String[] checkDomains(String[] domains, String action) {
+        return domains;
+    }
+
+    // from MBeanServerConnection
+    public String getDefaultDomain() throws IOException {
+        try {
+            return source().getDefaultDomain();
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,282 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+import com.sun.jmx.defaults.JmxProperties;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanRegistrationException;
+
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.namespace.JMXNamespaces;
+
+
+/**
+ * An RoutingProxy narrows on a given name space in a
+ * source object implementing MBeanServerConnection.
+ * It is used to implement
+ * {@code JMXNamespaces.narrowToNamespace(...)}.
+ * This abstract class has two concrete subclasses:
+ * <p>{@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.</p>
+ * <p>{@link RoutingServerProxy}: to cd in an MBeanServer.</p>
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public abstract class RoutingProxy<T extends MBeanServerConnection>
+        extends RoutingMBeanServerConnection<T> {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    // The source MBeanServerConnection
+    private final T source;
+
+    // The name space we're narrowing to (usually some name space in
+    // the source MBeanServerConnection
+    private final String                sourceNs;
+
+    // The name space we pretend to be mounted in (usually "")
+    private final String                targetNs;
+
+    // The name of the JMXNamespace that handles the source name space
+    private final ObjectName            handlerName;
+    private final ObjectNameRouter      router;
+    final boolean forwardsContext;
+    private volatile String             defaultDomain = null;
+
+    /**
+     * Creates a new instance of RoutingProxy
+     */
+    protected RoutingProxy(T source,
+                          String sourceNs,
+                          String targetNs,
+                          boolean forwardsContext) {
+        if (source == null) throw new IllegalArgumentException("null");
+        this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
+
+        // Usually sourceNs is not null, except when implementing
+        // Client Contexts
+        //
+        if (sourceNs.equals("")) {
+            this.handlerName = null;
+        } else {
+            // System.err.println("sourceNs: "+sourceNs);
+            this.handlerName =
+                JMXNamespaces.getNamespaceObjectName(this.sourceNs);
+            try {
+                // System.err.println("handlerName: "+handlerName);
+                if (!source.isRegistered(handlerName))
+                    throw new IllegalArgumentException(sourceNs +
+                            ": no such name space");
+            } catch (IOException x) {
+                throw new IllegalArgumentException("source stale: "+x,x);
+            }
+        }
+        this.source = source;
+        this.targetNs = (targetNs==null?"":
+            JMXNamespaces.normalizeNamespaceName(targetNs));
+        this.router =
+                new ObjectNameRouter(this.targetNs,this.sourceNs);
+        this.forwardsContext = forwardsContext;
+
+        if (LOG.isLoggable(Level.FINER))
+            LOG.finer("RoutingProxy for " + this.sourceNs + " created");
+    }
+
+    @Override
+    public T source() { return source; }
+
+    ObjectNameRouter getObjectNameRouter() {
+// TODO: uncomment this when contexts are added
+//        if (forwardsContext)
+//            return ObjectNameRouter.wrapWithContext(router);
+//        else
+            return router;
+    }
+
+    @Override
+    public ObjectName toSource(ObjectName targetName)
+        throws MalformedObjectNameException {
+        if (targetName == null) return null;
+        if (targetName.getDomain().equals("") && targetNs.equals("")) {
+            try {
+                if (defaultDomain == null)
+                    defaultDomain = getDefaultDomain();
+            } catch(Exception x) {
+                LOG.log(Level.FINEST,"Failed to get default domain",x);
+            }
+            if (defaultDomain != null)
+                targetName = targetName.withDomain(defaultDomain);
+        }
+        final ObjectNameRouter r = getObjectNameRouter();
+        return r.toSourceContext(targetName,true);
+    }
+
+    @Override
+    protected ObjectName newSourceMBeanName(ObjectName targetName)
+        throws MBeanRegistrationException {
+        if (targetName != null) return super.newSourceMBeanName(targetName);
+
+        // OK => we can accept null if sourceNs is empty.
+        if (sourceNs.equals("")) return null;
+
+        throw new MBeanRegistrationException(
+                new IllegalArgumentException(
+                "Can't use null ObjectName with namespaces"));
+    }
+
+    @Override
+    public ObjectName toTarget(ObjectName sourceName)
+        throws MalformedObjectNameException {
+        if (sourceName == null) return null;
+        final ObjectNameRouter r = getObjectNameRouter();
+        return r.toTargetContext(sourceName,false);
+    }
+
+    private Object getAttributeFromHandler(String attributeName)
+            throws IOException {
+
+        try {
+            return source().getAttribute(handlerName,attributeName);
+         } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+         } catch (IOException x) {
+             throw x;
+         } catch (MBeanException ex) {
+             throw new IOException("Failed to get "+attributeName+": "+
+                     ex.getMessage(),
+                     ex.getTargetException());
+         } catch (AttributeNotFoundException ex) {
+             throw new IOException("Failed to get "+attributeName+": "+
+                     ex.getMessage(),ex);
+         } catch (InstanceNotFoundException ex) {
+             throw new IOException("Failed to get "+attributeName+": "+
+                     ex.getMessage(),ex);
+         } catch (ReflectionException ex) {
+             throw new IOException("Failed to get "+attributeName+": "+
+                     ex.getMessage(),ex);
+         }
+    }
+
+    // We cannot call getMBeanCount() on the underlying
+    // MBeanServerConnection, because it would return the number of
+    // 'top-level' MBeans, not the number of MBeans in the name space
+    // we are narrowing to. Instead we're calling getMBeanCount() on
+    // the JMXNamespace that handles the source name space.
+    //
+    // There is however one particular case when the sourceNs is empty.
+    // In that case, there's no handler - and the 'source' is the top
+    // level namespace. In that particular case, handlerName will be null,
+    // and we directly invoke the top level source().
+    // This later complex case is only used when implementing ClientContexts.
+    //
+    @Override
+    public Integer getMBeanCount() throws IOException {
+        try {
+            if (handlerName == null) return source().getMBeanCount();
+            return (Integer) getAttributeFromHandler("MBeanCount");
+         } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+         }
+    }
+
+    // We cannot call getDomains() on the underlying
+    // MBeanServerConnection, because it would return the domains of
+    // 'top-level' MBeans, not the domains of MBeans in the name space
+    // we are narrowing to. Instead we're calling getDomains() on
+    // the JMXNamespace that handles the source name space.
+    //
+    // There is however one particular case when the sourceNs is empty.
+    // In that case, there's no handler - and the 'source' is the top
+    // level namespace. In that particular case, handlerName will be null,
+    // and we directly invoke the top level source().
+    // This later complex case is only used when implementing ClientContexts.
+    //
+    @Override
+    public String[] getDomains() throws IOException {
+        try {
+            if (handlerName == null) return source().getDomains();
+            return (String[]) getAttributeFromHandler("Domains");
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    // We cannot call getDefaultDomain() on the underlying
+    // MBeanServerConnection, because it would return the default domain of
+    // 'top-level' namespace, not the default domain in the name space
+    // we are narrowing to. Instead we're calling getDefaultDomain() on
+    // the JMXNamespace that handles the source name space.
+    //
+    // There is however one particular case when the sourceNs is empty.
+    // In that case, there's no handler - and the 'source' is the top
+    // level namespace. In that particular case, handlerName will be null,
+    // and we directly invoke the top level source().
+    // This later complex case is only used when implementing ClientContexts.
+    //
+    @Override
+    public String getDefaultDomain() throws IOException {
+        try {
+            if (handlerName == null) {
+                defaultDomain = source().getDefaultDomain();
+            } else {
+                defaultDomain =(String)
+                        getAttributeFromHandler("DefaultDomain");
+            }
+            return defaultDomain;
+        } catch (RuntimeException ex) {
+            throw makeCompliantRuntimeException(ex);
+        }
+    }
+
+    public String getSourceNamespace() {
+        return sourceNs;
+    }
+
+    public String getTargetNamespace() {
+        return targetNs;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()+", sourceNs="+
+                sourceNs + (targetNs.equals("")?"":
+                    (" mounted on targetNs="+targetNs));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,602 @@
+/*
+ * 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 com.sun.jmx.namespace;
+
+
+import com.sun.jmx.mbeanserver.Util;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.loading.ClassLoaderRepository;
+import javax.management.namespace.JMXNamespaces;
+
+/**
+ * A RoutingServerProxy is an MBeanServer proxy that proxies a
+ * source name space in a source MBeanServer.
+ * It wraps a source MBeanServer, and rewrites routing ObjectNames.
+ * It is typically use for implementing 'cd' operations, and
+ * will add the source name space to routing ObjectNames at input,
+ * and remove it at output.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ *
+ * @since 1.7
+ */
+public class RoutingServerProxy
+        extends RoutingProxy<MBeanServer>
+        implements MBeanServer {
+
+    /**
+     * Creates a new instance of RoutingServerProxy
+     */
+    public RoutingServerProxy(MBeanServer source,
+                           String sourceNs) {
+        this(source,sourceNs,"",false);
+    }
+
+    public RoutingServerProxy(MBeanServer source,
+                                String sourceNs,
+                                String targetNs,
+                                boolean forwardsContext) {
+        super(source,sourceNs,targetNs,forwardsContext);
+    }
+
+    /**
+     * This method is called each time an IOException is raised when
+     * trying to forward an operation to the underlying
+     * MBeanServerConnection, as a result of calling
+     * {@link #getMBeanServerConnection()} or as a result of invoking the
+     * operation on the returned connection.
+     * Subclasses may redefine this method if they need to perform any
+     * specific handling of IOException (logging etc...).
+     * @param x The raised IOException.
+     * @param method The name of the method in which the exception was
+     *        raised. This is one of the methods of the MBeanServer
+     *        interface.
+     * @return A RuntimeException that should be thrown by the caller.
+     *         In this default implementation, this is an
+     *         {@link UndeclaredThrowableException} wrapping <var>x</var>.
+     **/
+    protected RuntimeException handleIOException(IOException x,
+                                                 String method) {
+        return Util.newRuntimeIOException(x);
+    }
+
+
+    //--------------------------------------------
+    //--------------------------------------------
+    //
+    // Implementation of the MBeanServer interface
+    //
+    //--------------------------------------------
+    //--------------------------------------------
+    @Override
+    public void addNotificationListener(ObjectName name,
+                                        NotificationListener listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+        throws InstanceNotFoundException {
+        try {
+            super.addNotificationListener(name, listener,
+                                                 filter, handback);
+        } catch (IOException x) {
+            throw handleIOException(x,"addNotificationListener");
+        }
+    }
+
+    @Override
+    public void addNotificationListener(ObjectName name,
+                                        ObjectName listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+        throws InstanceNotFoundException {
+        try {
+            super.addNotificationListener(name, listener,
+                                                 filter, handback);
+        } catch (IOException x) {
+            throw handleIOException(x,"addNotificationListener");
+        }
+    }
+
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException {
+        try {
+            return super.createMBean(className, name);
+        } catch (IOException x) {
+            throw handleIOException(x,"createMBean");
+        }
+    }
+
+    @Override
+    public ObjectInstance createMBean(String className, ObjectName name,
+                                      Object params[], String signature[])
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException {
+        try {
+            return super.createMBean(className, name,
+                                     params, signature);
+        } catch (IOException x) {
+            throw handleIOException(x,"createMBean");
+        }
+    }
+
+    @Override
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      ObjectName loaderName)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException {
+        try {
+            return super.createMBean(className, name, loaderName);
+        } catch (IOException x) {
+            throw handleIOException(x,"createMBean");
+        }
+    }
+
+    @Override
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      ObjectName loaderName,
+                                      Object params[],
+                                      String signature[])
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException {
+        try {
+            return super.createMBean(className, name, loaderName,
+                                            params, signature);
+        } catch (IOException x) {
+            throw handleIOException(x,"createMBean");
+        }
+    }
+
+    /**
+     * @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[])
+     *                 MBeanServer}
+     **/
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+        throws InstanceNotFoundException, OperationsException {
+        final ObjectName sourceName = toSourceOrRuntime(name);
+        try {
+            return source().deserialize(sourceName,data);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    /**
+     * @deprecated see {@link MBeanServer#deserialize(String,byte[])
+     *                 MBeanServer}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+        throws OperationsException, ReflectionException {
+        try {
+            return source().deserialize(className,data);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    /**
+     * @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[])
+     *                 MBeanServer}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+                                         ObjectName loaderName,
+                                         byte[] data)
+        throws
+        InstanceNotFoundException,
+        OperationsException,
+        ReflectionException {
+        try {
+            return source().deserialize(className,loaderName,data);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    @Override
+    public Object getAttribute(ObjectName name, String attribute)
+        throws
+        MBeanException,
+        AttributeNotFoundException,
+        InstanceNotFoundException,
+        ReflectionException {
+        try {
+            return super.getAttribute(name, attribute);
+        } catch (IOException x) {
+            throw handleIOException(x,"getAttribute");
+        }
+    }
+
+    @Override
+    public AttributeList getAttributes(ObjectName name, String[] attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        try {
+            return super.getAttributes(name, attributes);
+        } catch (IOException x) {
+            throw handleIOException(x,"getAttributes");
+        }
+    }
+
+    public ClassLoader getClassLoader(ObjectName loaderName)
+        throws InstanceNotFoundException {
+        final ObjectName sourceName = toSourceOrRuntime(loaderName);
+        try {
+            return source().getClassLoader(sourceName);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+        throws InstanceNotFoundException {
+        final ObjectName sourceName = toSourceOrRuntime(mbeanName);
+        try {
+            return source().getClassLoaderFor(sourceName);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    public ClassLoaderRepository getClassLoaderRepository() {
+        try {
+            return source().getClassLoaderRepository();
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    @Override
+    public String getDefaultDomain() {
+        try {
+            return super.getDefaultDomain();
+        } catch (IOException x) {
+            throw handleIOException(x,"getDefaultDomain");
+        }
+    }
+
+    @Override
+    public String[] getDomains() {
+        try {
+            return super.getDomains();
+        } catch (IOException x) {
+            throw handleIOException(x,"getDomains");
+        }
+    }
+
+    @Override
+    public Integer getMBeanCount() {
+        try {
+            return super.getMBeanCount();
+        } catch (IOException x) {
+            throw handleIOException(x,"getMBeanCount");
+        }
+    }
+
+    @Override
+    public MBeanInfo getMBeanInfo(ObjectName name)
+        throws
+        InstanceNotFoundException,
+        IntrospectionException,
+        ReflectionException {
+        try {
+            return super.getMBeanInfo(name);
+        } catch (IOException x) {
+            throw handleIOException(x,"getMBeanInfo");
+        }
+    }
+
+    @Override
+    public ObjectInstance getObjectInstance(ObjectName name)
+        throws InstanceNotFoundException {
+        try {
+            return super.getObjectInstance(name);
+        } catch (IOException x) {
+            throw handleIOException(x,"getObjectInstance");
+        }
+    }
+
+    public Object instantiate(String className)
+        throws ReflectionException, MBeanException {
+        try {
+            return source().instantiate(className);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    public Object instantiate(String className,
+                              Object params[],
+                              String signature[])
+        throws ReflectionException, MBeanException {
+        try {
+            return source().instantiate(className,
+                    params,signature);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    public Object instantiate(String className, ObjectName loaderName)
+        throws ReflectionException, MBeanException,
+               InstanceNotFoundException {
+        final ObjectName srcLoaderName = toSourceOrRuntime(loaderName);
+        try {
+            return source().instantiate(className,srcLoaderName);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    public Object instantiate(String className, ObjectName loaderName,
+                              Object params[], String signature[])
+        throws ReflectionException, MBeanException,
+               InstanceNotFoundException {
+        final ObjectName srcLoaderName = toSourceOrRuntime(loaderName);
+        try {
+            return source().instantiate(className,srcLoaderName,
+                    params,signature);
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    @Override
+    public Object invoke(ObjectName name, String operationName,
+                         Object params[], String signature[])
+        throws
+        InstanceNotFoundException,
+        MBeanException,
+        ReflectionException {
+        try {
+            return super.invoke(name,operationName,params,signature);
+        } catch (IOException x) {
+            throw handleIOException(x,"invoke");
+        }
+    }
+
+    @Override
+    public boolean isInstanceOf(ObjectName name, String className)
+        throws InstanceNotFoundException {
+        try {
+            return super.isInstanceOf(name, className);
+        } catch (IOException x) {
+            throw handleIOException(x,"isInstanceOf");
+        }
+    }
+
+    @Override
+    public boolean isRegistered(ObjectName name) {
+        try {
+            return super.isRegistered(name);
+        } catch (IOException x) {
+            throw handleIOException(x,"isRegistered");
+        }
+    }
+
+    @Override
+    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+        try {
+            return super.queryMBeans(name, query);
+        } catch (IOException x) {
+            handleIOException(x,"queryMBeans");
+            return Collections.emptySet();
+        }
+    }
+
+    @Override
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        try {
+            return super.queryNames(name, query);
+        } catch (IOException x) {
+            handleIOException(x,"queryNames");
+            return Collections.emptySet();
+        }
+    }
+
+    public ObjectInstance registerMBean(Object object, ObjectName name)
+        throws
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        NotCompliantMBeanException {
+        final ObjectName sourceName = newSourceMBeanName(name);
+        try {
+            return processOutputInstance(
+                    source().registerMBean(object,sourceName));
+        } catch (RuntimeException x) {
+            throw makeCompliantRuntimeException(x);
+        }
+    }
+
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener);
+        } catch (IOException x) {
+            throw handleIOException(x,"removeNotificationListener");
+        }
+    }
+
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener,
+                                                    filter, handback);
+        } catch (IOException x) {
+            throw handleIOException(x,"removeNotificationListener");
+        }
+    }
+
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener);
+        } catch (IOException x) {
+            throw handleIOException(x,"removeNotificationListener");
+        }
+    }
+
+    @Override
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            super.removeNotificationListener(name, listener,
+                                                    filter, handback);
+        } catch (IOException x) {
+            throw handleIOException(x,"removeNotificationListener");
+        }
+    }
+
+    @Override
+    public void setAttribute(ObjectName name, Attribute attribute)
+        throws
+        InstanceNotFoundException,
+        AttributeNotFoundException,
+        InvalidAttributeValueException,
+        MBeanException,
+        ReflectionException {
+        try {
+            super.setAttribute(name, attribute);
+        } catch (IOException x) {
+            throw handleIOException(x,"setAttribute");
+        }
+    }
+
+    @Override
+    public AttributeList setAttributes(ObjectName name,
+                                       AttributeList attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        try {
+            return super.setAttributes(name, attributes);
+        } catch (IOException x) {
+            throw handleIOException(x,"setAttributes");
+        }
+    }
+
+    @Override
+    public void unregisterMBean(ObjectName name)
+        throws InstanceNotFoundException, MBeanRegistrationException {
+        try {
+           super.unregisterMBean(name);
+        } catch (IOException x) {
+            throw handleIOException(x,"unregisterMBean");
+        }
+    }
+
+
+    public static MBeanServer cd(MBeanServer source, String sourcePath) {
+        if (source == null) throw new IllegalArgumentException("null");
+        if (source.getClass().equals(RoutingServerProxy.class)) {
+            // cast is OK here, but findbugs complains unless we use class.cast
+            final RoutingServerProxy other =
+                    RoutingServerProxy.class.cast(source);
+            final String target = other.getTargetNamespace();
+
+            // Avoid multiple layers of serialization.
+            //
+            // We construct a new proxy from the original source instead of
+            // stacking a new proxy on top of the old one.
+            // - that is we replace
+            //      cd ( cd ( x, dir1), dir2);
+            // by
+            //      cd (x, dir1//dir2);
+            //
+            // We can do this only when the source class is exactly
+            //    NamespaceServerProxy.
+            //
+            if (target == null || target.equals("")) {
+                final String path =
+                    JMXNamespaces.concat(other.getSourceNamespace(),
+                    sourcePath);
+                return new RoutingServerProxy(other.source(),path,"",
+                                              other.forwardsContext);
+            }
+            // Note: we could do possibly something here - but it would involve
+            //       removing part of targetDir, and possibly adding
+            //       something to sourcePath.
+            //       Too complex to bother! => simply default to stacking...
+        }
+        return new RoutingServerProxy(source,sourcePath);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/package.html	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+  <head>
+    <title>The <code>com.sun.jmx.namespace</code> package</title>
+<!--
+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.
+-->
+  </head>
+    <body bgcolor="white">
+        <p>The <code>com.sun.jmx.namespace</code> package contains
+            sun specific implementation classes used to implement the
+            JMX namespaces. 
+        </p>
+        <p><b>DO NOT USE THESE CLASSES DIRECTLY</b></p>
+        <p><b>
+        This API is a Sun internal API and is subject to changes without notice.
+        </b></p>
+        <p>The public API through wich these proprietary classes can be 
+           invoked is located in <code>javax.management.namespace</code>
+           package.
+        </p>
+    </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,150 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * Class DefaultRewritingProcessor. Rewrite ObjectName in input & output
+ * parameters.
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+// We know that rewriting using serialization is costly.
+// This object tries to determine whether an object needs rewriting prior
+// to rewriting, and rewrites by creating a new object in those cases
+// where we know how to recreate a new object (e.g. a Notification).
+// Rewriting is however usually not used - so this object is just a
+// skeleton that eventually uses serialization...
+//
+class DefaultRewritingProcessor extends RewritingProcessor {
+
+
+    private static enum RewriteMode {
+        INPUT,  // Input from target to source  (parameters)
+        OUTPUT  // Output from source to target (results)
+    };
+
+    private final boolean identity;
+
+    public DefaultRewritingProcessor(String targetDirName) {
+        this(targetDirName,null);
+    }
+
+    /** Creates a new instance of SerialParamProcessor */
+    public DefaultRewritingProcessor(final String remove, final String add) {
+        super(new SerialRewritingProcessor(remove, add));
+        identity = remove.equals(add);
+    }
+
+    private ObjectName rewriteObjectName(RewriteMode mode,
+            ObjectName name) {
+        return changeContext(mode, name);
+    }
+
+    private ObjectInstance rewriteObjectInstance(RewriteMode mode,
+            ObjectInstance moi) {
+        final ObjectName srcName = moi.getObjectName();
+        final ObjectName targetName = changeContext(mode,srcName);
+        if (targetName == srcName) return moi;
+        return new ObjectInstance(targetName,moi.getClassName());
+    }
+
+
+    private Object processObject(RewriteMode mode, Object obj) {
+        if (obj == null) return null;
+
+        // Some things which will always needs rewriting:
+        // ObjectName, ObjectInstance, and Notifications.
+        // Take care of those we can handle here...
+        //
+        if (obj instanceof ObjectName)
+            return rewriteObjectName(mode,(ObjectName) obj);
+        else if (obj instanceof ObjectInstance)
+            return rewriteObjectInstance(mode,(ObjectInstance) obj);
+
+        // TODO: add other standard JMX classes - like e.g. MBeanInfo...
+        //
+
+        // Well, the object may contain an ObjectName => pass it to
+        // our serial rewriting delegate...
+        //
+        return processAnyObject(mode,obj);
+    }
+
+
+    private Object processAnyObject(RewriteMode mode, Object obj) {
+        switch (mode) {
+            case INPUT:
+                return super.rewriteInput(obj);
+            case OUTPUT:
+                return super.rewriteOutput(obj);
+            default: // can't happen.
+                throw new AssertionError();
+        }
+    }
+
+    private ObjectName changeContext(RewriteMode mode, ObjectName name) {
+        switch (mode) {
+            case INPUT:
+                return toSourceContext(name);
+            case OUTPUT:
+                return toTargetContext(name);
+            default: // can't happen.
+                throw new AssertionError();
+        }
+    }
+
+    @Override
+    public ObjectName toTargetContext(ObjectName srcName) {
+        if (identity) return srcName;
+        return super.toTargetContext(srcName);
+    }
+
+    @Override
+    public ObjectName toSourceContext(ObjectName targetName) {
+        if (identity) return targetName;
+        return super.toSourceContext(targetName);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T rewriteInput(T input) {
+        if (identity) return input;
+        return (T) processObject(RewriteMode.INPUT,input);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T rewriteOutput(T result) {
+        if (identity) return result;
+        return (T) processObject(RewriteMode.OUTPUT,result);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,74 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * Class RoutingOnlyProcessor. A RewritingProcessor that uses
+ * Java Serialization to rewrite ObjectNames contained in
+ * input & results...
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ *
+ * @since 1.7
+ */
+class IdentityProcessor extends RewritingProcessor {
+
+
+    /** Creates a new instance of SerialRewritingProcessor */
+    public IdentityProcessor() {
+    }
+
+    @Override
+    public <T> T rewriteOutput(T result) {
+        return result;
+    }
+
+    @Override
+    public <T> T rewriteInput(T input) {
+        return input;
+    }
+
+    @Override
+    public final ObjectName toTargetContext(ObjectName sourceName) {
+        return sourceName;
+    }
+
+    @Override
+    public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
+        return sourceMoi;
+    }
+
+    @Override
+    public final ObjectName toSourceContext(ObjectName targetName) {
+        return targetName;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,145 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+import com.sun.jmx.defaults.JmxProperties;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * The JMXNamespaceContext class is used to implement a thread local
+ * serialization / deserialization context for namespaces.
+ * <p>
+ * This class is consulted by {@link javax.management.ObjectName} at
+ * serialization / deserialization time.
+ * The serialization or deserialization context is established by
+ * by the {@link SerialRewritingProcessor} defined in this package.
+ * <p>
+ * These classes are Sun proprietary APIs, subject to change without
+ * notice. Do not use these classes directly.
+ * The public API to rewrite ObjectNames embedded in parameters is
+ * defined in {@link javax.management.namespace.JMXNamespaces}.
+ *
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public class JMXNamespaceContext {
+
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    public final String prefixToRemove;
+    public final String prefixToAdd;
+
+    private JMXNamespaceContext(String add, String remove) {
+        prefixToRemove = (remove==null?"":remove);
+        prefixToAdd    = (add==null?"":add);
+    }
+
+    private final static class SerialContext {
+        private JMXNamespaceContext serializationContext;
+        private JMXNamespaceContext deserializationContext;
+        public SerialContext(){
+            serializationContext = new JMXNamespaceContext("","");
+            deserializationContext = new JMXNamespaceContext("","");
+        }
+    }
+
+    private final static ThreadLocal<SerialContext> prefix =
+            new ThreadLocal<SerialContext>() {
+        @Override
+        protected SerialContext initialValue() {
+            return new SerialContext();
+        }
+    };
+
+    public static JMXNamespaceContext getSerializationContext() {
+        return prefix.get().serializationContext;
+    }
+
+    public static JMXNamespaceContext getDeserializationContext() {
+        return prefix.get().deserializationContext;
+    }
+
+    private static String[] setSerializationContext(String oldPrefix,
+            String newPrefix) {
+        final SerialContext c = prefix.get();
+        JMXNamespaceContext dc = c.serializationContext;
+        String[] old = {dc.prefixToRemove, dc.prefixToAdd};
+        c.serializationContext = new JMXNamespaceContext(newPrefix,oldPrefix);
+        return old;
+    }
+
+    private static String[] setDeserializationContext(String oldPrefix,
+            String newPrefix) {
+        final SerialContext c = prefix.get();
+        JMXNamespaceContext dc = c.deserializationContext;
+        String[] old = {dc.prefixToRemove, dc.prefixToAdd};
+        c.deserializationContext = new JMXNamespaceContext(newPrefix,oldPrefix);
+        return old;
+    }
+
+    static void serialize(ObjectOutputStream stream, Object obj,
+            String prefixToRemove, String prefixToAdd)
+            throws IOException {
+        final String[] old =
+                setSerializationContext(prefixToRemove,prefixToAdd);
+        try {
+            stream.writeObject(obj);
+        } finally {
+            try {
+                setSerializationContext(old[0],old[1]);
+            } catch (Exception x) {
+                LOG.log(Level.FINEST,
+                        "failed to restore serialization context",x);
+            }
+        }
+    }
+
+    static Object deserialize(ObjectInputStream stream,
+            String prefixToRemove,
+            String prefixToAdd)
+            throws IOException, ClassNotFoundException {
+        final String[] old =
+                setDeserializationContext(prefixToRemove,prefixToAdd);
+        try {
+            return stream.readObject();
+        } finally {
+            try {
+                setDeserializationContext(old[0],old[1]);
+            } catch (Exception x) {
+                LOG.log(Level.FINEST,
+                        "failed to restore serialization context",x);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,362 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * An object that can rewrite ObjectNames contained in input/output
+ * parameters when entering/leaving a {@link javax.management.namespace
+ * namespace}.
+ * <p>When entering a {@link javax.management.namespace
+ *    namespace}, the {@code namespace} prefix is stripped from
+ *    ObjectNames contained in input parameters. When leaving a
+ *    {@code namespace},
+ *    the {@code namespace} prefix is prepended to the ObjectNames contained in
+ *    the result parameters returned from that {@code namespace}.
+ * </p>
+ * <p>Objects that need to perform these operations usually use a
+ *    {@code RewritingProcessor} for that purpose.<br>
+ *    The {@code RewritingProcessor} allows a somewhat larger
+ *    transformation in which part of a prefix {@link #newRewritingProcessor
+ *    remove} can be replaced by another prefix {@link #newRewritingProcessor
+ *    add}. The transformation described above correspond to the case where
+ *    {@code remove} is the stripped {@link javax.management.namespace
+ *    namespace} prefix (removed when entering the {@code namespace}) and
+ *    {@code add} is the empty String {@code ""}.
+ *    <br>
+ *    It is interesting to note that {@link
+ *    javax.management.JMXNamespaces#narrowToNamespace narrowToNamespace}
+ *    operations use the inverse transformation (that is, {@code remove} is
+ *    the empty String {@code ""} and {@code add} is the {@link
+ *    javax.management.namespace namespace} prefix).
+ *    <br>
+ *    On a more general scale, {@link #rewriteInput rewriteInput} removes
+ *    {@link #newRewritingProcessor remove} and the prepend {@link
+ *     #newRewritingProcessor add}, and {@link #rewriteOutput rewriteOutput}
+ *    does the opposite, removing {@link #newRewritingProcessor add}, and
+ *    then adding {@link #newRewritingProcessor remove}.
+ *    <br>
+ *    An implementation of {@code RewritingProcessor} should make sure that
+ *    <code>rewriteInput(rewriteOutput(x,clp),clp)</code> and
+ *    <code>rewriteOutput(rewriteInput(x,clp),clp)</code> always return
+ *    {@code x} or an exact clone of {@code x}.
+ * </p>
+ * <p>A default implementation of {@code RewritingProcessor} based on
+ * Java Object Serialization can be
+ * obtained from {@link #newRewritingProcessor newRewritingProcessor}.
+ * </p>
+ * <p>
+ * By default, the instances of {@code RewritingProcessor} returned by
+ * {@link #newRewritingProcessor newRewritingProcessor} will rewrite
+ * ObjectNames contained in instances of classes they don't know about by
+ * serializing and then deserializing such object instances. This will
+ * happen even if such instances don't - or can't contain ObjectNames,
+ * because the default implementation of {@code RewritingProcessor} will
+ * not be able to determine whether instances of such classes can/do contain
+ * instance of ObjectNames before serializing/deserializing them.
+ * </p>
+ * <p>If you are using custom classes that the default implementation of
+ * {@code RewritingProcessor} don't know about, it can be interesting to
+ * prevent an instance of {@code RewritingProcessor} to serialize/deserialize
+ * instances of such classes for nothing. In that case, you could customize
+ * the behavior of such a {@code RewritingProcessor} by wrapping it in a
+ * custom subclass of {@code RewritingProcessor} as shown below:
+ * <pre>
+ * public class MyRewritingProcessor extends RewritingProcessor {
+ *      MyRewritingProcessor(String remove, String add) {
+ *          this(RewritingProcessor.newRewritingProcessor(remove,add));
+ *      }
+ *      MyRewritingProcessor(RewritingProcessor delegate) {
+ *          super(delegate);
+ *      }
+ *
+ *  <T> T rewriteInput(T input) {
+ *          if (input == null) return null;
+ *          if (MyClass.equals(input.getClass())) {
+ *              // I know that MyClass doesn't contain any ObjectName
+ *              return (T) input;
+ *          }
+ *          return super.rewriteInput(input);
+ *      }
+ *  <T> T rewriteOutput(T result) {
+ *          if (result == null) return null;
+ *          if (MyClass.equals(result.getClass())) {
+ *              // I know that MyClass doesn't contain any ObjectName
+ *              return (T) result;
+ *          }
+ *          return super.rewriteOutput(result);
+ *      }
+ * }
+ * </pre>
+ * </p>
+ * <p>Such a subclass may also provide an alternate way of rewriting
+ *    custom subclasses for which rewriting is needed - for instance:
+ * <pre>
+ * public class MyRewritingProcessor extends RewritingProcessor {
+ *      MyRewritingProcessor(String remove, String add) {
+ *          this(RewritingProcessor.newRewritingProcessor(remove,add));
+ *      }
+ *      MyRewritingProcessor(RewritingProcessor delegate) {
+ *          super(delegate);
+ *      }
+ *
+ *  <T> T rewriteInput(T input) {
+ *          if (input == null) return null;
+ *          if (MyClass.equals(input.getClass())) {
+ *              // I know that MyClass doesn't contain any ObjectName
+ *              return (T) input;
+ *          } else if (MyOtherClass.equals(input.getClass())) {
+ *              // Returns a new instance in which ObjectNames have been
+ *              // replaced.
+ *              final ObjectName aname = ((MyOtherClass)input).getName();
+ *              return (T) (new MyOtherClass(super.rewriteInput(aname)));
+ *          }
+ *          return super.rewriteInput(input,clp);
+ *      }
+ *  <T> T rewriteOutput(T result) {
+ *          if (result == null) return null;
+ *          if (MyClass.equals(result.getClass())) {
+ *              // I know that MyClass doesn't contain any ObjectName
+ *              return (T) result;
+ *          } else if (MyOtherClass.equals(result.getClass())) {
+ *              // Returns a new instance in which ObjectNames have been
+ *              // replaced.
+ *              final ObjectName aname = ((MyOtherClass)result).getName();
+ *              return (T) (new MyOtherClass(super.rewriteOutput(aname)));
+ *          }
+ *          return super.rewriteOutput(result,clp);
+ *      }
+ * }
+ * </pre>
+ * </p>
+ * <p>If your application only uses {@link javax.management.MXBean MXBeans},
+ * or MBeans using simple types, and doesn't define any custom subclass of
+ * {@link javax.management.Notification}, you should never write such
+ * such {@code RewitingProcessor} implementations.
+ * </p>
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+public abstract class RewritingProcessor {
+    /**
+     * A logger for this class.
+     **/
+    private final RewritingProcessor delegate;
+
+    /**
+     * Creates a new instance of RewritingProcessor.
+     * <p>This is equivalent to calling {@link
+     * #RewritingProcessor(RewritingProcessor) RewritingProcessor(null)}.
+     * </p>
+     **/
+    protected RewritingProcessor() {
+        this(null);
+    }
+
+    /**
+     * Creates a new instance of RewritingProcessor, with a delegate.
+     * @param delegate a {@code RewritingProcessor} to which all the
+     *        calls will be delegated. When implementing a subclass
+     *        of  {@code RewritingProcessor}, calling {@link
+     *        #rewriteInput super.rewriteInput} will invoke
+     *        {@code delegate.rewriteInput} and calling {@link
+     *        #rewriteOutput super.rewriteOutput} will invoke
+     *        {@code delegate.rewriteOutput}.
+     *
+     **/
+    protected RewritingProcessor(RewritingProcessor delegate) {
+        this.delegate = delegate;
+    }
+
+    /**
+     * Rewrites ObjectNames when {@link RewritingProcessor leaving} a {@link
+     * javax.management.namespace namespace}.
+     * <p>
+     * Returns {@code obj}, if it is known that {@code obj} doesn't contain
+     * any ObjectName, or a new copied instance of {@code obj} in which
+     * ObjectNames (if any) will have been rewritten, if {@code obj} contains
+     * ObjectNames, or if it is not known whether {@code obj} contains
+     * ObjectNames or not.
+     * </p>
+     * <p>
+     * The default implementation of this method is as follows: if the
+     * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
+     * null}, throws an {@link IllegalArgumentException}. Otherwise,
+     * returns {@code delegate.rewriteOutput(obj)}.
+     * </p>
+     * <p>This behavior can be overridden by subclasses as shown in this
+     * class {@link RewritingProcessor description}.
+     * </p>
+     * @param obj The result to be rewritten if needed.
+     *
+     * @return {@code obj}, or a clone of {@code obj} in which ObjectNames
+     *         have been rewritten. See this class {@link RewritingProcessor
+     *         description} for more details.
+     * @throws IllegalArgumentException if this implementation does not know
+     *         how to rewrite the object.
+     **/
+    public <T> T rewriteOutput(T obj) {
+        if (obj == null) return null;
+        if (delegate != null)
+            return delegate.rewriteOutput(obj);
+        throw new IllegalArgumentException("can't rewrite "+
+                obj.getClass().getName());
+    }
+
+    /**
+     * Rewrites ObjectNames when {@link RewritingProcessor entering} a {@link
+     * javax.management.namespace namespace}.
+     * <p>
+     * Returns {@code obj}, if it is known that {@code obj} doesn't contain
+     * any ObjectName, or a new copied instance of {@code obj} in which
+     * ObjectNames (if any) will have been rewritten, if {@code obj} contains
+     * ObjectNames, or if it is not known whether {@code obj} contains
+     * ObjectNames or not.
+     * </p>
+     * <p>
+     * The default implementation of this method is as follows: if the
+     * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
+     * null}, throws an {@link IllegalArgumentException}. Otherwise,
+     * returns {@code delegate.rewriteInput(obj)}.
+     * </p>
+     * <p>This behavior can be overridden by subclasses as shown in this
+     * class {@link RewritingProcessor description}.
+     * </p>
+     * @param obj The result to be rewritten if needed.
+     * @return {@code obj}, or a clone of {@code obj} in which ObjectNames
+     *         have been rewritten. See this class {@link RewritingProcessor
+     *         description} for more details.
+     * @throws IllegalArgumentException if this implementation does not know
+     *         how to rewrite the object.
+     **/
+    public <T> T rewriteInput(T obj) {
+        if (obj == null) return null;
+        if (delegate != null)
+            return delegate.rewriteInput(obj);
+        throw new IllegalArgumentException("can't rewrite "+
+                obj.getClass().getName());
+    }
+
+    /**
+     * Translate a routing ObjectName from the target (calling) context to
+     * the source (called) context when {@link RewritingProcessor entering} a
+     * {@link javax.management.namespace namespace}.
+     * <p>
+     * The default implementation of this method is as follows: if the
+     * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
+     * null}, throws an {@link IllegalArgumentException}. Otherwise,
+     * returns {@code delegate.toSourceContext(targetName)}.
+     * </p>
+     * <p>This behavior can be overridden by subclasses as shown in this
+     * class {@link RewritingProcessor description}.
+     * </p>
+     * @param targetName The routing target ObjectName to translate.
+     * @return The ObjectName translated to the source context.
+     * @throws IllegalArgumentException if this implementation does not know
+     *         how to rewrite the object.
+     **/
+    public ObjectName toSourceContext(ObjectName targetName) {
+        if (delegate != null)
+            return delegate.toSourceContext(targetName);
+        throw new IllegalArgumentException("can't rewrite targetName: "+
+               " no delegate.");
+    }
+
+    /**
+     * Translate an ObjectName returned from the source context into
+     * the target (calling) context when {@link RewritingProcessor leaving} a
+     * {@link javax.management.namespace namespace}.
+     * <p>
+     * The default implementation of this method is as follows: if the
+     * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
+     * null}, throws an {@link IllegalArgumentException}. Otherwise,
+     * returns {@code delegate.toTargetContext(sourceName)}.
+     * </p>
+     * <p>This behavior can be overridden by subclasses as shown in this
+     * class {@link RewritingProcessor description}.
+     * </p>
+     * @param sourceName The routing source ObjectName to translate to the
+     *        target context.
+     * @return The ObjectName translated to the target context.
+     * @throws IllegalArgumentException if this implementation does not know
+     *         how to rewrite the object.
+     **/
+    public ObjectName toTargetContext(ObjectName sourceName) {
+        if (delegate != null)
+            return delegate.toTargetContext(sourceName);
+        throw new IllegalArgumentException("can't rewrite sourceName: "+
+               " no delegate.");
+    }
+
+    /**
+     * Translate an ObjectInstance returned from the source context into
+     * the target (calling) context when {@link RewritingProcessor leaving} a
+     * {@link javax.management.namespace namespace}.
+     * <p>
+     * The default implementation of this method is as follows: if the
+     * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
+     * null}, throws an {@link IllegalArgumentException}. Otherwise,
+     * returns {@code delegate.toTargetContext(sourceMoi)}.
+     * </p>
+     * <p>This behavior can be overridden by subclasses as shown in this
+     * class {@link RewritingProcessor description}.
+     * </p>
+     * @param sourceMoi The routing source ObjectInstance to translate.
+     * @return The ObjectInstance translated to the target context.
+     * @throws IllegalArgumentException if this implementation does not know
+     *         how to rewrite the object.
+     **/
+    public ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
+        if (delegate != null)
+            return delegate.toTargetContext(sourceMoi);
+        throw new IllegalArgumentException("can't rewrite sourceName: "+
+               " no delegate.");
+    }
+
+    /**
+     * Creates a new default instance of {@link RewritingProcessor}.
+     * @param remove The prefix to remove from {@link ObjectName ObjectNames}
+     *        when {@link RewritingProcessor entering} the {@link
+     *        javax.management.namespace namespace}.
+     * @param add The prefix to add to {@link ObjectName ObjectNames}
+     *        when {@link RewritingProcessor entering} the {@link
+     *        javax.management.namespace namespace} (this is performed
+     *        after having removed the {@code remove} prefix.
+     * @return A new {@link RewritingProcessor} processor object that will
+     *         perform the requested operation, using Java serialization if
+     *         necessary.
+     **/
+    public static RewritingProcessor newRewritingProcessor(String remove,
+            String add) {
+        return new DefaultRewritingProcessor(remove,add);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,74 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+import com.sun.jmx.namespace.ObjectNameRouter;
+
+
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * Class RoutingOnlyProcessor. A RewritingProcessor that uses
+ * Java Serialization to rewrite ObjectNames contained in
+ * input and results...
+ *
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+class RoutingOnlyProcessor extends RewritingProcessor {
+
+    final ObjectNameRouter router;
+
+    public RoutingOnlyProcessor(String targetDirName) {
+        this(targetDirName,null);
+    }
+
+    /** Creates a new instance of RoutingOnlyProcessor */
+    public RoutingOnlyProcessor(final String remove, final String add) {
+        super(new IdentityProcessor());
+        if (remove == null || add == null)
+            throw new IllegalArgumentException("Null argument");
+        router = new ObjectNameRouter(remove,add);
+    }
+
+    @Override
+    public final ObjectName toTargetContext(ObjectName sourceName) {
+        return router.toTargetContext(sourceName,false);
+    }
+
+    @Override
+    public final ObjectName toSourceContext(ObjectName targetName) {
+        return router.toSourceContext(targetName,false);
+    }
+
+    @Override
+    public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
+        return router.toTargetContext(sourceMoi,false);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,172 @@
+/*
+ * 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 com.sun.jmx.namespace.serial;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import javax.management.ObjectName;
+
+/**
+ * Class SerialRewritingProcessor. A RewritingProcessor that uses
+ * Java Serialization to rewrite ObjectNames contained in
+ * input & results...
+ * <p><b>
+ * This API is a Sun internal API and is subject to changes without notice.
+ * </b></p>
+ * @since 1.7
+ */
+class SerialRewritingProcessor extends RewritingProcessor {
+
+
+    private static class CloneOutput extends ObjectOutputStream {
+        Queue<Class<?>> classQueue = new LinkedList<Class<?>>();
+
+        CloneOutput(OutputStream out) throws IOException {
+            super(out);
+        }
+
+        @Override
+        protected void annotateClass(Class<?> c) {
+            classQueue.add(c);
+        }
+
+        @Override
+        protected void annotateProxyClass(Class<?> c) {
+            classQueue.add(c);
+        }
+    }
+
+    private static class CloneInput extends ObjectInputStream {
+        private final CloneOutput output;
+
+        CloneInput(InputStream in, CloneOutput output) throws IOException {
+            super(in);
+            this.output = output;
+        }
+
+        @Override
+        protected Class<?> resolveClass(ObjectStreamClass osc)
+        throws IOException, ClassNotFoundException {
+            Class<?> c = output.classQueue.poll();
+            String expected = osc.getName();
+            String found = (c == null) ? null : c.getName();
+            if (!expected.equals(found)) {
+                throw new InvalidClassException("Classes desynchronized: " +
+                        "found " + found + " when expecting " + expected);
+            }
+            return c;
+        }
+
+        @Override
+        protected Class<?> resolveProxyClass(String[] interfaceNames)
+        throws IOException, ClassNotFoundException {
+            return output.classQueue.poll();
+        }
+    }
+
+
+    final String targetPrefix;
+    final String sourcePrefix;
+    final boolean identity;
+
+
+    public SerialRewritingProcessor(String targetDirName) {
+        this(targetDirName,null);
+    }
+
+    /** Creates a new instance of SerialRewritingProcessor */
+    public SerialRewritingProcessor(final String remove, final String add) {
+        super(new RoutingOnlyProcessor(remove,add));
+        this.targetPrefix = remove;
+        this.sourcePrefix = add;
+        identity = targetPrefix.equals(sourcePrefix);
+    }
+
+    private <T> T  switchContext(T result, String from,String to)
+            throws IOException, ClassNotFoundException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final CloneOutput ostream = new CloneOutput(baos);
+
+        JMXNamespaceContext.serialize(ostream,result,from,null);
+        ostream.flush();
+
+        final byte[] bytes = baos.toByteArray();
+        final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+        final CloneInput istream = new CloneInput(bais, ostream);
+        @SuppressWarnings("unchecked")
+        final T clone = (T) JMXNamespaceContext.deserialize(istream,null,to);
+        return clone;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T rewriteOutput(T result) {
+        if (identity) return result;
+        return (T) processOutput(result);
+    }
+
+    private Object processOutput(Object result) {
+        try {
+            if (result instanceof ObjectName)
+                return toTargetContext((ObjectName) result);
+            return switchContext(result,sourcePrefix,targetPrefix);
+        } catch (ClassNotFoundException x) {
+            throw new IllegalArgumentException("Can't process result: "+x,x);
+        } catch (IOException x) {
+            throw new IllegalArgumentException("Can't process result: "+x,x);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T rewriteInput(T input) {
+        if (identity) return input;
+        return (T) processInput(input);
+    }
+
+    private Object processInput(Object input) {
+        try {
+            if (input instanceof ObjectName)
+                return toSourceContext((ObjectName) input);
+            return switchContext(input,targetPrefix,sourcePrefix);
+        } catch (ClassNotFoundException x) {
+            throw new IllegalArgumentException("Can't process input: "+x,x);
+        } catch (IOException x) {
+            throw new IllegalArgumentException("Can't process input: "+x,x);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/jmx/namespace/serial/package.html	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+  <head>
+    <title>The <code>com.sun.jmx.namespace.serial</code> package</title>
+<!--
+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.
+-->
+  </head>
+  <body bgcolor="white">
+  <p>The <code>com.sun.jmx.namespace.serial</code> package contains
+     sun specific implementation classes used to switch namespace
+     prefixes in ObjectName during serialization.
+  </p>
+  <p><b>NEVER USE THESE CLASSES DIRECTLY</b></p>
+  <p><b>
+  This API is a Sun internal API and is subject to changes without notice.
+  </b></p>
+  <p>The public API through which these proprietary classes can be invoked is
+      located in <code>javax.management.namespace.JMXNamespaces</code>
+  </p>
+  </body>
+</html>
--- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java	Thu Sep 04 14:46:36 2008 +0200
@@ -57,6 +57,7 @@
 
 public class ServerNotifForwarder {
 
+
     public ServerNotifForwarder(MBeanServer mbeanServer,
                                 Map env,
                                 NotificationBuffer notifBuffer,
@@ -85,7 +86,8 @@
 
         // Explicitly check MBeanPermission for addNotificationListener
         //
-        checkMBeanPermission(name, "addNotificationListener");
+        checkMBeanPermission(getMBeanServerName(),
+                mbeanServer, name, "addNotificationListener");
         if (notificationAccessController != null) {
             notificationAccessController.addNotificationListener(
                 connectionId, name, getSubject());
@@ -155,7 +157,8 @@
 
         // Explicitly check MBeanPermission for removeNotificationListener
         //
-        checkMBeanPermission(name, "removeNotificationListener");
+        checkMBeanPermission(getMBeanServerName(),
+                mbeanServer, name, "removeNotificationListener");
         if (notificationAccessController != null) {
             notificationAccessController.removeNotificationListener(
                 connectionId, name, getSubject());
@@ -330,13 +333,7 @@
      * Explicitly check the MBeanPermission for
      * the current access control context.
      */
-    private void checkMBeanPermission(final ObjectName name,
-        final String actions)
-            throws InstanceNotFoundException, SecurityException {
-        checkMBeanPermission(mbeanServer, name, actions);
-    }
-
-    public static void checkMBeanPermission(
+    public static void checkMBeanPermission(String serverName,
             final MBeanServer mbs, final ObjectName name, final String actions)
             throws InstanceNotFoundException, SecurityException {
         SecurityManager sm = System.getSecurityManager();
@@ -355,7 +352,9 @@
                 throw (InstanceNotFoundException) extractException(e);
             }
             String classname = oi.getClassName();
-            MBeanPermission perm = new MBeanPermission(classname,
+            MBeanPermission perm = new MBeanPermission(
+                serverName,
+                classname,
                 null,
                 name,
                 actions);
@@ -370,8 +369,8 @@
                                               TargetedNotification tn) {
         try {
             if (checkNotificationEmission) {
-                checkMBeanPermission(
-                        name, "addNotificationListener");
+                checkMBeanPermission(getMBeanServerName(),
+                        mbeanServer, name, "addNotificationListener");
             }
             if (notificationAccessController != null) {
                 notificationAccessController.fetchNotification(
@@ -433,11 +432,27 @@
         }
     }
 
+    private String getMBeanServerName() {
+        if (mbeanServerName != null) return mbeanServerName;
+        else return (mbeanServerName = getMBeanServerName(mbeanServer));
+    }
+
+    private static String getMBeanServerName(final MBeanServer server) {
+        final PrivilegedAction<String> action = new PrivilegedAction<String>() {
+            public String run() {
+                return Util.getMBeanServerSecurityName(server);
+            }
+        };
+        return AccessController.doPrivileged(action);
+    }
+
+
     //------------------
     // PRIVATE VARIABLES
     //------------------
 
     private MBeanServer mbeanServer;
+    private volatile String mbeanServerName;
 
     private final String connectionId;
 
--- a/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,7 @@
 
 package com.sun.jmx.remote.util;
 
+import com.sun.jmx.defaults.JmxProperties;
 import com.sun.jmx.event.EventClientFactory;
 
 import java.lang.reflect.InvocationHandler;
@@ -45,6 +46,7 @@
 import javax.management.ObjectName;
 import javax.management.event.EventClient;
 import javax.management.event.EventClientDelegate;
+import javax.management.namespace.JMXNamespaces;
 
 /**
  * Class EventClientConnection - a {@link Proxy} that wraps an
@@ -63,12 +65,10 @@
     /**
      * A logger for this class.
      **/
-    private static final Logger LOG =
-            Logger.getLogger(EventClientConnection.class.getName());
+    private static final Logger LOG = JmxProperties.NOTIFICATION_LOGGER;
 
-    private static final String NAMESPACE_SEPARATOR = "//";
     private static final int NAMESPACE_SEPARATOR_LENGTH =
-            NAMESPACE_SEPARATOR.length();
+            JMXNamespaces.NAMESPACE_SEPARATOR.length();
 
     /**
      * Creates a new {@code EventClientConnection}.
@@ -212,9 +212,9 @@
         }
 
         final ObjectName mbean = (ObjectName) args[0];
-        final EventClient client = getEventClient();
+        final EventClient evtClient = getEventClient();
 
-        // Fails if client is null AND the MBean we try to listen to is
+        // Fails if evtClient is null AND the MBean we try to listen to is
         // in a subnamespace. We fail here because we know this will not
         // work.
         //
@@ -222,15 +222,15 @@
         // earlier agent (JDK 1.6 or earlier), then the EventClient will
         // be null (we can't use the event service with earlier JDKs).
         //
-        // In principle a null client indicates that the remote VM is of
+        // In principle a null evtClient indicates that the remote VM is of
         // an earlier version, in which case it shouldn't contain any namespace.
         //
-        // So having a null client AND an MBean contained in a namespace is
+        // So having a null evtClient AND an MBean contained in a namespace is
         // clearly an error case.
         //
-        if (client == null) {
+        if (evtClient == null) {
             final String domain = mbean.getDomain();
-            final int index = domain.indexOf(NAMESPACE_SEPARATOR);
+            final int index = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
             if (index > -1 && index <
                     (domain.length()-NAMESPACE_SEPARATOR_LENGTH)) {
                 throw new UnsupportedOperationException(method.getName()+
@@ -256,9 +256,9 @@
             final NotificationFilter filter = (NotificationFilter) args[2];
             final Object handback = args[3];
 
-            if (client != null) {
+            if (evtClient != null) {
                 // general case
-                client.addNotificationListener(mbean,listener,filter,handback);
+                evtClient.addNotificationListener(mbean,listener,filter,handback);
             } else {
                 // deprecated case. Only works for mbean in local namespace.
                 connection.addNotificationListener(mbean,listener,filter,
@@ -274,9 +274,9 @@
 
             switch (nargs) {
             case 2:
-                if (client != null) {
+                if (evtClient != null) {
                     // general case
-                    client.removeNotificationListener(mbean,listener);
+                    evtClient.removeNotificationListener(mbean,listener);
                 } else {
                     // deprecated case. Only works for mbean in local namespace.
                     connection.removeNotificationListener(mbean, listener);
@@ -286,8 +286,8 @@
             case 4:
                 NotificationFilter filter = (NotificationFilter) args[2];
                 Object handback = args[3];
-                if (client != null) {
-                    client.removeNotificationListener(mbean,
+                if (evtClient != null) {
+                    evtClient.removeNotificationListener(mbean,
                                                       listener,
                                                       filter,
                                                       handback);
--- a/src/share/classes/javax/management/InstanceNotFoundException.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/InstanceNotFoundException.java	Thu Sep 04 14:46:36 2008 +0200
@@ -51,4 +51,16 @@
     public InstanceNotFoundException(String message) {
         super(message);
     }
+
+    /**
+     * Constructor for the frequent case where the message is the ObjectName
+     * of the missing MBean.
+     *
+     * @param name the ObjectName of the missing MBean.
+     *
+     * @since 1.7
+     */
+    public InstanceNotFoundException(ObjectName name) {
+        this(name.toString());
+    }
 }
--- a/src/share/classes/javax/management/MBeanPermission.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanPermission.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,7 @@
 
 package javax.management;
 
+import com.sun.jmx.mbeanserver.Util;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.security.Permission;
@@ -42,10 +43,10 @@
  * permission that you <em>need</em>.  When a sensitive operation is
  * being checked for permission, an MBeanPermission is constructed
  * representing the permission you need.  The operation is only
- * allowed if the permissions you have {@link #implies imply} the
+ * allowed if the permissions you have {@linkplain #implies imply} the
  * permission you need.</p>
  *
- * <p>An MBeanPermission contains four items of information:</p>
+ * <p>An MBeanPermission contains five items of information:</p>
  *
  * <ul>
  *
@@ -57,6 +58,23 @@
  *
  * <p>The action is returned by {@link #getActions()}.</p>
  *
+ * <li id="MBeanServerName"><p>The <em>MBean Server name</em>.</p>
+ *
+ * <p>For a permission you need, this is the {@linkplain
+ * javax.management.MBeanServerFactory#getMBeanServerName
+ * name of the MBeanServer}
+ * containing the <a href="#MBeanName">MBean</a> for which the MBean
+ * permission is checked.</p>
+ *
+ * <p>For a permission you have, this is either the  {@linkplain
+ * javax.management.MBeanServerFactory#getMBeanServerName
+ * name of the MBeanServer} in which the <a href="#MBeanName">MBean</a>
+ * you have this permission for must be registered,
+ * or a pattern against which that MBean Server name will be matched.<br>
+ * An {@code mbeanServerName} pattern can also be empty or the single
+ * character {@code "*"}, both of which will match any {@code MBeanServer} name.
+ * </p>
+ *
  * <li><p>The <em>class name</em>.</p>
  *
  * <p>For a permission you need, this is the class name of an MBean
@@ -88,7 +106,7 @@
  * or operation you can access, or it is empty or the single character
  * "<code>*</code>", both of which grant access to any member.</p>
  *
- * <li><p>The <em>object name</em>.</p>
+ * <li id="MBeanName"><p>The <em>object name</em>.</p>
  *
  * <p>For a permission you need, this is the {@link ObjectName} of the
  * MBean you are accessing.  For operations that do not reference a
@@ -103,15 +121,15 @@
  * </ul>
  *
  * <p>If you have an MBeanPermission, it allows operations only if all
- * four of the items match.</p>
+ * five of the items match.</p>
  *
- * <p>The class name, member, and object name can be written together
- * as a single string, which is the <em>name</em> of this permission.
+ * <p>The MBean Server name, class name, member, and object name can be written
+ * together as a single string, which is the <em>name</em> of this permission.
  * The name of the permission is the string returned by {@link
  * Permission#getName() getName()}.  The format of the string is:</p>
  *
  * <blockquote>
- * <code>className#member[objectName]</code>
+ * <code>mbeanServerName::className#member[objectName]</code>
  * </blockquote>
  *
  * <p>The object name is written using the usual syntax for {@link
@@ -119,15 +137,18 @@
  * <code>]</code>.  It is terminated by a <code>]</code> character
  * that is the last character in the string.</p>
  *
- * <p>One or more of the <code>className</code>, <code>member</code>,
- * or <code>objectName</code> may be omitted.  If the
- * <code>member</code> is omitted, the <code>#</code> may be too (but
+ * <p>One or more of the <code>mbeanServerName</code>, <code>className</code>,
+ * <code>member</code>, or <code>objectName</code> may be omitted. If the
+ * <code>mbeanServerName</code> is omitted, the <code>::</code> may be too (but
+ * does not have to be).
+ * If the <code>member</code> is omitted, the <code>#</code> may be too (but
  * does not have to be).  If the <code>objectName</code> is omitted,
  * the <code>[]</code> may be too (but does not have to be).  It is
- * not legal to omit all three items, that is to have a <em>name</em>
+ * not legal to omit all four items, that is to have a <em>name</em>
  * that is the empty string.</p>
  *
- * <p>One or more of the <code>className</code>, <code>member</code>,
+ * <p>One or more of the <code>mbeanServerName</code>,  <code>className</code>,
+ * <code>member</code>,
  * or <code>objectName</code> may be the character "<code>-</code>",
  * which is equivalent to a null value.  A null value is implied by
  * any value (including another null value) but does not imply any
@@ -247,6 +268,13 @@
     private transient ObjectName objectName;
 
     /**
+     * The name of the MBeanServer in which this permission is checked, or
+     * granted.  If null, is implied by any MBean Server name
+     * but does not imply any non-null MBean Server name.
+     */
+    private transient String mbeanServerName;
+
+    /**
      * Parse <code>actions</code> parameter.
      */
     private void parseActions() {
@@ -283,6 +311,13 @@
             throw new IllegalArgumentException("MBeanPermission name " +
                                                "cannot be empty");
 
+        final int sepIndex = name.indexOf("::");
+        if (sepIndex < 0) {
+            setMBeanServerName("*");
+        } else {
+            setMBeanServerName(name.substring(0,sepIndex));
+        }
+
         /* The name looks like "class#member[objectname]".  We subtract
            elements from the right as we parse, so after parsing the
            objectname we have "class#member" and after parsing the
@@ -290,11 +325,14 @@
 
         // Parse ObjectName
 
-        int openingBracket = name.indexOf("[");
+
+        final int start = (sepIndex<0)?0:sepIndex+2;
+        int openingBracket = name.indexOf("[",start);
         if (openingBracket == -1) {
             // If "[on]" missing then ObjectName("*:*")
             //
             objectName = ObjectName.WILDCARD;
+            name = name.substring(start);
         } else {
             if (!name.endsWith("]")) {
                 throw new IllegalArgumentException("MBeanPermission: " +
@@ -305,11 +343,11 @@
             } else {
                 // Create ObjectName
                 //
+                String on = name.substring(openingBracket + 1,
+                                           name.length() - 1);
                 try {
                     // If "[]" then ObjectName("*:*")
                     //
-                    String on = name.substring(openingBracket + 1,
-                                               name.length() - 1);
                     if (on.equals(""))
                         objectName = ObjectName.WILDCARD;
                     else if (on.equals("-"))
@@ -320,11 +358,11 @@
                     throw new IllegalArgumentException("MBeanPermission: " +
                                                        "The target name does " +
                                                        "not specify a valid " +
-                                                       "ObjectName");
+                                                       "ObjectName", e);
                 }
             }
 
-            name = name.substring(0, openingBracket);
+            name = name.substring(start, openingBracket);
         }
 
         // Parse member
@@ -348,8 +386,9 @@
      * Assign fields based on className, member, and objectName
      * parameters.
      */
-    private void initName(String className, String member,
-                          ObjectName objectName) {
+    private void initName(String mbeanServerName, String className,
+                          String member, ObjectName objectName) {
+        setMBeanServerName(mbeanServerName);
         setClassName(className);
         setMember(member);
         this.objectName = objectName;
@@ -381,19 +420,30 @@
             this.member = member;
     }
 
+    private void setMBeanServerName(String mbeanServerName) {
+        if (mbeanServerName == null || mbeanServerName.equals("-")) {
+            this.mbeanServerName = null;
+        } else if (mbeanServerName.equals("")) {
+            this.mbeanServerName = "*";
+        } else {
+            this.mbeanServerName = mbeanServerName;
+        }
+    }
+
+
     /**
      * <p>Create a new MBeanPermission object with the specified target name
      * and actions.</p>
      *
      * <p>The target name is of the form
-     * "<code>className#member[objectName]</code>" where each part is
-     * optional.  It must not be empty or null.</p>
+     * "<code>mbeanServerName::className#member[objectName]</code>" where
+     * each part is optional.  It must not be empty or null.</p>
      *
      * <p>The actions parameter contains a comma-separated list of the
      * desired actions granted on the target name.  It must not be
      * empty or null.</p>
      *
-     * @param name the triplet "className#member[objectName]".
+     * @param name the quadruplet "mbeanServerName::className#member[objectName]".
      * @param actions the action string.
      *
      * @exception IllegalArgumentException if the <code>name</code> or
@@ -418,6 +468,12 @@
      * optional.  This will be the result of {@link #getName()} on the
      * resultant MBeanPermission.</p>
      *
+     * <p>This corresponds to a permission granted for all
+     *    MBean servers present in the JVM and is equivalent to
+     *    {@link #MBeanPermission(String,String,String,ObjectName,String)
+     *           MBeanPermission("*",className,member,objectName,actions)}.
+     * </p>
+     *
      * <p>The actions parameter contains a comma-separated list of the
      * desired actions granted on the target name.  It must not be
      * empty or null.</p>
@@ -439,17 +495,67 @@
                            String member,
                            ObjectName objectName,
                            String actions) {
+        this("*",className,member,objectName,actions);
+    }
 
-        super(makeName(className, member, objectName));
-        initName(className, member, objectName);
+    /**
+     * <p>Create a new MBeanPermission object with the specified target name
+     * (MBean Server name, class name, member, object name) and actions.</p>
+     *
+     * <p>The MBean Server name, class name, member and object name
+     * parameters define a target name of the form
+     * "<code>mbeanServerName::className#member[objectName]</code>" where each
+     * part is optional.  This will be the result of {@link #getName()} on the
+     * resultant MBeanPermission.
+     * If the <code>mbeanServerName</code> is empty or exactly {@code "*"}, then
+     * "{@code mbeanServerName::}" is omitted in that result.
+     * </p>
+     *
+     * <p>The actions parameter contains a comma-separated list of the
+     * desired actions granted on the target name.  It must not be
+     * empty or null.</p>
+     *
+     * @param mbeanServerName the name of the {@code MBeanServer} to which this
+     * permission applies.
+     * May be null or <code>"-"</code>, which represents an MBeanServer name
+     * that is implied by any MBeanServer name but does not imply any other
+     * MBeanServer name.
+     * @param className the class name to which this permission applies.
+     * May be null or <code>"-"</code>, which represents a class name
+     * that is implied by any class name but does not imply any other
+     * class name.
+     * @param member the member to which this permission applies.  May
+     * be null or <code>"-"</code>, which represents a member that is
+     * implied by any member but does not imply any other member.
+     * @param objectName the object name to which this permission
+     * applies.  May be null, which represents an object name that is
+     * implied by any object name but does not imply any other object
+     * name.
+     * @param actions the action string.
+     *
+     * @since 1.7
+     */
+    public MBeanPermission(String mbeanServerName,
+                           String className,
+                           String member,
+                           ObjectName objectName,
+                           String actions) {
+
+        super(makeName(mbeanServerName,className, member, objectName));
+        initName(mbeanServerName,className, member, objectName);
 
         this.actions = actions;
         parseActions();
     }
 
-    private static String makeName(String className, String member,
+    private static String makeName(String mbeanServerName, String className,
+                                   String member,
                                    ObjectName objectName) {
         final StringBuilder name = new StringBuilder();
+        if (mbeanServerName == null)
+            mbeanServerName = "-";
+        if (!mbeanServerName.equals("") && !mbeanServerName.equals("*"))
+            name.append(mbeanServerName).append("::");
         if (className == null)
             className = "-";
         name.append(className);
@@ -991,6 +1097,9 @@
      *
      * <li> <i>p</i> is an instance of MBeanPermission; and</li>
      *
+     * <li> <i>p</i> has a null mbeanServerName or <i>p</i>'s mbeanServerName
+     * matches this object's mbeanServerName; and</li>
+     *
      * <li> <i>p</i> has a null className or <i>p</i>'s className
      * matches this object's className; and</li>
      *
@@ -1004,6 +1113,13 @@
      *
      * </ul>
      *
+     * <p>If this object's mbeanServerName is a pattern, then <i>p</i>'s
+     *    mbeanServerName is matched against that pattern. An empty
+     *    mbeanServerName is equivalent to "{@code *}". A null
+     *    mbeanServerName is equivalent to "{@code -}".</p>
+     * <p>If this object's mbeanServerName is "<code>*</code>" or is
+     * empty, <i>p</i>'s mbeanServerName always matches it.</p>
+     *
      * <p>If this object's className is "<code>*</code>", <i>p</i>'s
      * className always matches it.  If it is "<code>a.*</code>", <i>p</i>'s
      * className matches it if it begins with "<code>a.</code>".</p>
@@ -1050,6 +1166,12 @@
 
         // Target name
         //
+        // The 'mbeanServerName' check is true iff:
+        // 1) the mbeanServerName in 'this' permission is omitted or "*", or
+        // 2) the mbeanServerName in 'that' permission is omitted or "*", or
+        // 3) the mbeanServerName in 'this' permission does pattern
+        //    matching with the mbeanServerName in 'that' permission.
+        //
         // The 'className' check is true iff:
         // 1) the className in 'this' permission is omitted or "*", or
         // 2) the className in 'that' permission is omitted or "*", or
@@ -1076,6 +1198,17 @@
            expect that "that" contains a wildcard, since it is a
            needed permission.  So we assume that.classNameExactMatch.  */
 
+        if (that.mbeanServerName == null) {
+            // bottom is implied
+        } else if (this.mbeanServerName == null) {
+            // bottom implies nothing but itself
+            return false;
+        } else if (that.mbeanServerName.equals(this.mbeanServerName)) {
+            // exact match
+        } else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) {
+            return false; // no match
+        }
+
         if (that.classNamePrefix == null) {
             // bottom is implied
         } else if (this.classNamePrefix == null) {
--- a/src/share/classes/javax/management/MBeanServer.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServer.java	Thu Sep 04 14:46:36 2008 +0200
@@ -42,7 +42,7 @@
  *
  * <p>User code does not usually implement this interface.  Instead,
  * an object that implements this interface is obtained with one of
- * the methods in the {@link MBeanServerFactory} class.</p>
+ * the methods in the {@link javax.management.MBeanServerFactory} class.</p>
  *
  * <p>Every MBean which is added to the MBean server becomes
  * manageable: its attributes and operations become remotely
@@ -62,8 +62,12 @@
  * <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.</p>
  *
  * <p id="security">An object obtained from the {@link
- * MBeanServerFactory#createMBeanServer(String) createMBeanServer} or
- * {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer}
+ * MBeanServerFactory#createMBeanServer(String) createMBeanServer}, {@link
+ * MBeanServerFactory#createNamedMBeanServer(String,String) createNamedMBeanServer},
+ * {@link
+ * MBeanServerFactory#newMBeanServer(String) newMBeanServer}, or
+ * {@link
+ * MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer}
  * methods of the {@link MBeanServerFactory} class applies security
  * checks to its methods, as follows.</p>
  *
@@ -73,9 +77,26 @@
  *
  * <p>Assuming that there is a security manager, or that the
  * implementation chooses to make checks anyway, the checks are made
- * as detailed below.  In what follows, <code>className</code> is the
+ * as detailed below.
+ * In what follows, and unless otherwise specified:
+ * </p>
+ * <ul><li><code>className</code> is the
  * string returned by {@link MBeanInfo#getClassName()} for the target
- * MBean.</p>
+ * MBean,</li>
+ * <li>{@code mbeanServerName} is the
+ * {@linkplain MBeanServerFactory#getMBeanServerName name of the
+ * MBean Server} in which the target MBean is registered. This is the
+ * value returned by {@link MBeanServerFactory#getMBeanServerName
+ * MBeanServerFactory.getMBeanServerName(MBeanServer)}, and
+ * is usually the {@code mbeanServerName} parameter that was supplied
+ * to the {@link
+ * MBeanServerFactory#createNamedMBeanServer(String,String)
+ * createNamedMBeanServer} or {@link
+ * MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer}
+ * methods of the {@link MBeanServerFactory} when the MBeanServer was created,
+ * or {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if
+ * no name was supplied.
+ * </li></ul>
  *
  * <p>If a security check fails, the method throws {@link
  * SecurityException}.</p>
@@ -89,78 +110,86 @@
  *
  * <li><p>For the {@link #invoke invoke} method, the caller's
  * permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, operationName, name, "invoke")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, operationName, name, "invoke")}.
+ * </p>
  *
  * <li><p>For the {@link #getAttribute getAttribute} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, attribute, name, "getAttribute")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, attribute, name,
+ * "getAttribute")}.</p>
  *
  * <li><p>For the {@link #getAttributes getAttributes} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "getAttribute")}.
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName,className, null, name, "getAttribute")}.
  * Additionally, for each attribute <em>a</em> in the {@link
  * AttributeList}, if the caller's permissions do not imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, <em>a</em>, name, "getAttribute")}, the
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, <em>a</em>, name,
+ * "getAttribute")}, the
  * MBean server will behave as if that attribute had not been in the
  * supplied list.</p>
  *
  * <li><p>For the {@link #setAttribute setAttribute} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, attrName, name, "setAttribute")}, where
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, attrName, name,
+ * "setAttribute")}, where
  * <code>attrName</code> is {@link Attribute#getName()
  * attribute.getName()}.</p>
  *
  * <li><p>For the {@link #setAttributes setAttributes} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "setAttribute")}.
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "setAttribute")}.
  * Additionally, for each attribute <em>a</em> in the {@link
  * AttributeList}, if the caller's permissions do not imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, <em>a</em>, name, "setAttribute")}, the
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, <em>a</em>, name,
+ * "setAttribute")}, the
  * MBean server will behave as if that attribute had not been in the
  * supplied list.</p>
  *
  * <li><p>For the <code>addNotificationListener</code> methods,
  * the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name,
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name,
  * "addNotificationListener")}.</p>
  *
  * <li><p>For the <code>removeNotificationListener</code> methods,
  * the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name,
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name,
  * "removeNotificationListener")}.</p>
  *
  * <li><p>For the {@link #getMBeanInfo getMBeanInfo} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "getMBeanInfo")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "getMBeanInfo")}.
+ * </p>
  *
  * <li><p>For the {@link #getObjectInstance getObjectInstance} method,
  * the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "getObjectInstance")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name,
+ * "getObjectInstance")}.</p>
  *
  * <li><p>For the {@link #isInstanceOf isInstanceOf} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "isInstanceOf")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "isInstanceOf")}.
+ * </p>
  *
  * <li><p>For the {@link #queryMBeans queryMBeans} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(null, null, name, "queryMBeans")}.
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, null, null, null, "queryMBeans")}.
  * Additionally, for each MBean that matches <code>name</code>,
  * if the caller's permissions do not imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "queryMBeans")}, the
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "queryMBeans")}, the
  * MBean server will behave as if that MBean did not exist.</p>
  *
  * <p>Certain query elements perform operations on the MBean server.
@@ -179,10 +208,10 @@
  *
  * <li><p>For the {@link #getDomains getDomains} method, the caller's
  * permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(null, null, name, "getDomains")}.  Additionally,
- * for each domain <var>d</var> in the returned array, if the caller's
- * permissions do not imply {@link
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, null, null, null, "getDomains")}.
+ * Additionally, for each domain <var>d</var> in the returned array, if the
+ * caller's permissions do not imply {@link
  * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
  * MBeanPermission(null, null, new ObjectName("<var>d</var>:x=x"),
  * "getDomains")}, the domain is eliminated from the array.  Here,
@@ -191,21 +220,22 @@
  *
  * <li><p>For the {@link #getClassLoader getClassLoader} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, loaderName,
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, loaderName,
  * "getClassLoader")}.</p>
  *
  * <li><p>For the {@link #getClassLoaderFor getClassLoaderFor} method,
  * the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, mbeanName,
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, mbeanName,
  * "getClassLoaderFor")}.</p>
  *
  * <li><p>For the {@link #getClassLoaderRepository
  * getClassLoaderRepository} method, the caller's permissions must
  * imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(null, null, null, "getClassLoaderRepository")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, null, null, null,
+ * "getClassLoaderRepository")}.</p>
  *
  * <li><p>For the deprecated <code>deserialize</code> methods, the
  * required permissions are the same as for the methods that replace
@@ -213,15 +243,15 @@
  *
  * <li><p>For the <code>instantiate</code> methods, the caller's
  * permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, null, "instantiate")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, null, "instantiate")},
+ * where {@code className} is the name of the class which is to
+ * be instantiated.</p>
  *
  * <li><p>For the {@link #registerMBean registerMBean} method, the
  * caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "registerMBean")}.  Here
- * <code>className</code> is the string returned by {@link
- * MBeanInfo#getClassName()} for an object of this class.
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "registerMBean")}.
  *
  * <p>If the <code>MBeanPermission</code> check succeeds, the MBean's
  * class is validated by checking that its {@link
@@ -241,8 +271,9 @@
  *
  * <li><p>For the {@link #unregisterMBean unregisterMBean} method,
  * the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "unregisterMBean")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "unregisterMBean")}.
+ * </p>
  *
  * </ul>
  *
--- a/src/share/classes/javax/management/MBeanServerDelegate.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServerDelegate.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,7 +25,9 @@
 
 package javax.management;
 
+import com.sun.jmx.defaults.JmxProperties;
 import com.sun.jmx.defaults.ServiceName;
+import com.sun.jmx.mbeanserver.Util;
 
 /**
  * Represents  the MBean server from the management point of view.
@@ -39,6 +41,7 @@
 
     /** The MBean server agent identification.*/
     private String mbeanServerId ;
+    private String mbeanServerName;
 
     /** The NotificationBroadcasterSupport object that sends the
         notifications */
@@ -68,6 +71,7 @@
     public MBeanServerDelegate () {
         stamp = getStamp();
         broadcaster = new NotificationBroadcasterSupport() ;
+        mbeanServerName=null;
     }
 
 
@@ -82,14 +86,103 @@
             try {
                 localHost = java.net.InetAddress.getLocalHost().getHostName();
             } catch (java.net.UnknownHostException e) {
+                JmxProperties.MISC_LOGGER.finest("Can't get local host name, " +
+                        "using \"localhost\" instead. Cause is: "+e);
                 localHost = "localhost";
             }
-            mbeanServerId = localHost + "_" + stamp;
+            mbeanServerId =
+                    Util.insertMBeanServerName(localHost + "_" + stamp,
+                    mbeanServerName);
         }
         return mbeanServerId;
     }
 
     /**
+     * The name of the MBeanServer.
+     * @return The name of the MBeanServer, or {@value
+     * javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if no
+     * name was specified.
+     *
+     * @since 1.7
+     * @see #setMBeanServerName
+     */
+    public synchronized String getMBeanServerName() {
+        if (Util.isMBeanServerNameUndefined(mbeanServerName))
+            return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
+        return mbeanServerName;
+    }
+
+    /**
+     * Sets the name of the MBeanServer. The name will be embedded into the
+     * {@link #getMBeanServerId MBeanServerId} using the following format:<br>
+     * {@code mbeanServerId: <mbeanServerId>;mbeanServerName=<mbeanServerName>}
+     * <p>The characters {@code ':'} (colon), {@code ';'} (semicolon ),
+     * {@code '*'} (star) and {@code '?'} (question mark) are not legal in an
+     * MBean Server name.</p>
+     * <p>For instance, if the {@code mbeanServerName} provided is
+     * {@code "com.mycompany.myapp.server1"}, and the original
+     * {@code MBeanServerId} was {@code "myhost_1213353064145"},
+     * then {@code mbeanServerName} will be
+     * embedded in the {@code MBeanServerId} - and the new value of the
+     * {@code MBeanServerId} will be:
+     * </p>
+     * <pre>
+     *       "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"
+     * </pre>
+     * <p>Note: The {@code mbeanServerName} is usually set by the
+     *   {@code MBeanServerFactory}. It is set only once, before the
+     *   MBean Server is returned by the factory. Once the MBean Server name is
+     *   set, it is not possible to change it.
+     * </p>
+     * @param mbeanServerName The MBeanServer name.
+     * @throws IllegalArgumentException if the MBeanServerName is already set
+     *         to a different value, or if the provided name contains
+     *         illegal characters, or if the provided name is {@code ""}
+     *         (the empty string) or "-" (dash).
+     * @throws UnsupportedOperationException if this object is of a legacy
+     *         subclass of MBeanServerDelegate which overrides {@link
+     *         #getMBeanServerId()}
+     *         in a way that doesn't support setting an MBeanServer name.
+     * @see MBeanServerFactory#getMBeanServerName
+     * @since 1.7
+     */
+    public synchronized void setMBeanServerName(String mbeanServerName) {
+        // Sets the name on the delegate. For complex backward
+        // compatibility reasons it is not possible to give the
+        // name to the MBeanServerDelegate constructor.
+        //
+        // The method setMBeanServerName() will call getMBeanServerId()
+        // to check that the name is accurately set in the MBeanServerId.
+        // If not (which could happen if a custom MBeanServerDelegate
+        // implementation overrides getMBeanServerId() and was not updated
+        // with respect to JMX 2.0 spec), this method will throw an
+        // IllegalStateException...
+
+        // will fail if mbeanServerName is illegal
+        final String name = Util.checkServerName(mbeanServerName);
+
+        // can only set mbeanServerDelegate once.
+        if (this.mbeanServerName != null && !this.mbeanServerName.equals(name))
+            throw new IllegalArgumentException(
+                    "MBeanServerName already set to a different value");
+
+        this.mbeanServerName = name;
+
+        // will fail if mbeanServerId already has a different mbeanServerName
+        mbeanServerId =
+           Util.insertMBeanServerName(getMBeanServerId(),name);
+
+        // check that we don't have a subclass which overrides
+        // getMBeanServerId() without setting mbeanServerName
+        if (!name.equals(
+                Util.extractMBeanServerName(getMBeanServerId())))
+            throw new UnsupportedOperationException(
+                    "Can't set MBeanServerName in MBeanServerId - " +
+                    "unsupported by "+this.getClass().getName()+"?");
+        // OK: at this point we know that we have correctly set mbeanServerName.
+    }
+
+    /**
      * Returns the full name of the JMX specification implemented
      * by this product.
      *
@@ -210,15 +303,8 @@
      *
      * @since 1.6
      */
-    public static final ObjectName DELEGATE_NAME;
-    static {
-        try {
-            DELEGATE_NAME =
-                new ObjectName("JMImplementation:type=MBeanServerDelegate");
-        } catch (MalformedObjectNameException e) {
-            throw new Error("Can't initialize delegate name", e);
-        }
-    }
+    public static final ObjectName DELEGATE_NAME =
+            Util.newObjectName("JMImplementation:type=MBeanServerDelegate");
 
     /* Return a timestamp that is monotonically increasing even if
        System.currentTimeMillis() isn't (for example, if you call this
--- a/src/share/classes/javax/management/MBeanServerFactory.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServerFactory.java	Thu Sep 04 14:46:36 2008 +0200
@@ -25,16 +25,19 @@
 
 package javax.management;
 
+import com.sun.jmx.defaults.JmxProperties;
 import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER;
 import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
-import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
 import com.sun.jmx.mbeanserver.GetPropertyAction;
+import com.sun.jmx.mbeanserver.Util;
 import java.security.AccessController;
 import java.security.Permission;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.logging.Level;
 import javax.management.loading.ClassLoaderRepository;
 
+
 /**
  * <p>Provides MBean server references.  There are no instances of
  * this class.</p>
@@ -80,10 +83,53 @@
  * returned by the default MBeanServerBuilder implementation, for the purpose
  * of e.g. adding an additional security layer.</p>
  *
+ * <p id="MBeanServerName">Since version 2.0 of the JMX API, when creating
+ * an MBeanServer,
+ * it is possible to specify an {@linkplain #getMBeanServerName
+ * MBean Server name}.
+ * To create an MBean Server with a name, the MBeanServerFactory provides two
+ * new methods:</p>
+ * <ul><li>{@link #createNamedMBeanServer
+ * createNamedMBeanServer(mbeanServerName, defaultDomain)}: creates a named
+ * MBeanServer and keeps an internal reference to the created object. The
+ * MBeanServer can be later retrieved using {@link #findMBeanServer
+ * findMBeanServer(mbeanServerId)} or
+ * {@link #findMBeanServerByName findMBeanServerByName(mbeanServerName)}, and
+ * can be released through {@link
+ * #releaseMBeanServer releaseMBeanServer(mbeanServer)}.</li>
+ * <li>{@link #newNamedMBeanServer
+ * newNamedMBeanServer(mbeanServerName, defaultDomain)}:
+ * creates a named MBeanServer without keeping any internal reference to the
+ * named server.</li>
+ * </ul>
+ * <p>The name of the MBeanServer is stored in the
+ * {@linkplain MBeanServerDelegate MBean Server delegate MBean}
+ * and is embedded in its {@link MBeanServerDelegate#getMBeanServerId
+ * MBeanServerId} attribute.</p>
+ * <p>The name of the MBeanServer is particularly useful when
+ * <a href="MBeanServer.html#security">MBean permissions</a> are checked:
+ * it makes it
+ * possible to distinguish between an MBean named "X" in MBeanServer named
+ * "M1", and another MBean of the same name "X" in another MBeanServer named
+ * "M2".</p>
+ * <p>When naming MBean servers it is recommended to use a name that starts
+ * with a Java package name. It is also recommended that the default domain and
+ * the MBeanServer name be the same.</p>
+ *
  * @since 1.5
  */
 public class MBeanServerFactory {
 
+    /**
+     * The <a href="#MBeanServerName">MBean Server name</a> that will be
+     * checked by a <a href="MBeanServer.html#security">permission you need</a>
+     * when checking access to an MBean registered in an MBeanServer for
+     * which no MBeanServer name was specified.
+     *
+     * @since 1.7
+     */
+    public final static String DEFAULT_MBEANSERVER_NAME = "default";
+
     /*
      * There are no instances of this class so don't generate the
      * default public constructor.
@@ -222,13 +268,78 @@
      * <code>javax.management.builder.initial</code> exists and can be
      * instantiated but is not assignment compatible with {@link
      * MBeanServerBuilder}.
+     *
+     * @see #createNamedMBeanServer
      */
     public static MBeanServer createMBeanServer(String domain)  {
-        checkPermission("createMBeanServer");
+        return createMBeanServer(null,domain);
+    }
 
-        final MBeanServer mBeanServer = newMBeanServer(domain);
-        addMBeanServer(mBeanServer);
-        return mBeanServer;
+    /**
+     * <p>Return a new object implementing the {@link MBeanServer}
+     * interface with the specified
+     * <a href="#MBeanServerName">MBean Server name</a>
+     * and default domain name. The given MBean server name
+     * is used in <a href="MBeanServer.html#security">security checks</a>, and
+     * can also be used to {@linkplain #findMBeanServerByName(java.lang.String)
+     * find an MBeanServer by name}. The given
+     * domain name is used as the domain part in the ObjectName of
+     * MBeans when the domain is specified by the user is null.</p>
+     *
+     * <p>The MBeanServer reference is internally kept. This will
+     * allow <CODE>findMBeanServer</CODE> to return a reference to
+     * this MBeanServer object.</p>
+     *
+     * @param mbeanServerName the name for the created
+     * MBeanServer.  This is the name that will be included in the
+     * {@linkplain MBeanPermission permission you need} when checking
+     * <a href="MBeanServer.html#security">MBean Permissions</a> for accessing
+     * an MBean registered in the returned MBeanServer. The characters
+     * {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star)
+     * and  {@code '?'} are not legal.
+     * It is recommended that the {@code mbeanServerName}
+     * be unique in the context of a JVM, and in the form of a java package
+     * identifier. If {@code mbeanServerName} is {@code null} then the created
+     * MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used.
+     * Calling {@code createNamedMBeanServer(null,domain)} is equivalent
+     * to calling {@link #createMBeanServer(String) createMBeanServer(domain)}.
+     *
+     * @param domain the default domain name for the created
+     * MBeanServer.  This is the value that will be returned by {@link
+     * MBeanServer#getDefaultDomain}. If a non null mbeanServerName is given,
+     * it is recommended to pass the same value as default domain.
+     *
+     * @return the newly created MBeanServer.
+     *
+     * @exception SecurityException if there is a SecurityManager and
+     * the caller's permissions do not include or imply <code>{@link
+     * MBeanServerPermission}("createMBeanServer")</code>.
+     *
+     * @exception JMRuntimeException if the property
+     * <code>javax.management.builder.initial</code> exists but the
+     * class it names cannot be instantiated through a public
+     * no-argument constructor; or if the instantiated builder returns
+     * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
+     * newMBeanServerDelegate} or {@link
+     * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
+     *
+     * @exception ClassCastException if the property
+     * <code>javax.management.builder.initial</code> exists and can be
+     * instantiated but is not assignment compatible with {@link
+     * MBeanServerBuilder}.
+     *
+     * @exception IllegalArgumentException if the specified
+     * {@code mbeanServerName} is empty, or is {@code "-"}, or contains a
+     * character which is not legal.
+     *
+     * @exception UnsupportedOperationException if the specified
+     * {@code mbeanServerName} cannot be set.
+     *
+     * @since 1.7
+     */
+    public static MBeanServer createNamedMBeanServer(String mbeanServerName,
+            String domain)  {
+        return createMBeanServer(mbeanServerName, domain);
     }
 
     /**
@@ -307,6 +418,88 @@
      * MBeanServerBuilder}.
      */
     public static MBeanServer newMBeanServer(String domain)  {
+        return newMBeanServer(null,domain);
+    }
+
+    /**
+     * <p>Return a new object implementing the MBeanServer interface
+     * with the specified <a href="#MBeanServerName">MBean server name</a>
+     * and default domain name, without keeping an
+     * internal reference to this new object.  The given MBean server name
+     * is used in <a href="MBeanServer.html#security">security checks</a>.
+     * The given domain name
+     * is used as the domain part in the ObjectName of MBeans when the
+     * domain is specified by the user is null.</p>
+     *
+     * <p>No reference is kept. <CODE>findMBeanServer</CODE> and
+     * <CODE>findMBeanServerByName</CODE> will not
+     * be able to return a reference to this MBeanServer object, but
+     * the garbage collector will be able to remove the MBeanServer
+     * object when it is no longer referenced.</p>
+     *
+     * @param mbeanServerName the name for the created
+     * MBeanServer.  This is the name that will be included in the
+     * {@linkplain MBeanPermission permission you need} when checking
+     * <a href="MBeanServer.html#security">MBean Permissions</a> for accessing
+     * an MBean registered in the returned MBeanServer. The characters
+     * {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star)
+     * and  {@code '?'} are not legal.
+     * It is recommended that the mbeanServerName
+     * be unique in the context of a JVM, and in the form of a java package
+     * identifier. If {@code mbeanServerName} is {@code null} then the created
+     * MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used.
+     * Calling {@code newNamedMBeanServer(null,domain)} is equivalent
+     * to calling {@link #newMBeanServer(String) newMBeanServer(domain)}.
+     *
+     * @param domain the default domain name for the created
+     * MBeanServer.  This is the value that will be returned by {@link
+     * MBeanServer#getDefaultDomain}.
+     *
+     * @return the newly created MBeanServer.
+     *
+     * @exception SecurityException if there is a SecurityManager and the
+     * caller's permissions do not include or imply <code>{@link
+     * MBeanServerPermission}("newMBeanServer")</code>.
+     *
+     * @exception JMRuntimeException if the property
+     * <code>javax.management.builder.initial</code> exists but the
+     * class it names cannot be instantiated through a public
+     * no-argument constructor; or if the instantiated builder returns
+     * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
+     * newMBeanServerDelegate} or {@link
+     * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
+     *
+     * @exception ClassCastException if the property
+     * <code>javax.management.builder.initial</code> exists and can be
+     * instantiated but is not assignment compatible with {@link
+     * MBeanServerBuilder}.
+     *
+     * @exception IllegalArgumentException if the specified
+     * {@code mbeanServerName} is empty, or is {@code "-"},
+     * or contains a character which is not legal.
+     *
+     * @exception UnsupportedOperationException if the specified
+     * {@code mbeanServerName} cannot be set.
+     *
+     * @since 1.7
+     */
+    public static MBeanServer newNamedMBeanServer(String mbeanServerName,
+            String domain)  {
+        return newMBeanServer(mbeanServerName, domain);
+    }
+
+    private static MBeanServer createMBeanServer(String mbeanServerName,
+            String domain)  {
+        checkPermission("createMBeanServer");
+
+        final MBeanServer mBeanServer =
+                newMBeanServer(mbeanServerName,domain);
+        addMBeanServer(mBeanServer);
+        return mBeanServer;
+    }
+
+    private static MBeanServer newMBeanServer(String mbeanServerName,
+            String domain) {
         checkPermission("newMBeanServer");
 
         // Get the builder. Creates a new one if necessary.
@@ -316,20 +509,50 @@
 
         synchronized(mbsBuilder) {
             final MBeanServerDelegate delegate  =
-                mbsBuilder.newMBeanServerDelegate();
+                    mbsBuilder.newMBeanServerDelegate();
             if (delegate == null) {
                 final String msg =
-                    "MBeanServerBuilder.newMBeanServerDelegate() " +
-                    "returned null";
+                        "MBeanServerBuilder.newMBeanServerDelegate() " +
+                        "returned null";
                 throw new JMRuntimeException(msg);
             }
+
+            // Sets the name on the delegate. For complex backward
+            // compatibility reasons it is not possible to give the
+            // name to the MBeanServerDelegate constructor.
+            //
+            // The method setMBeanServerName() will call getMBeanServerId()
+            // to check that the name is accurately set in the MBeanServerId.
+            // If not (which could happen if a custom MBeanServerDelegate
+            // implementation overrides getMBeanServerId() and was not updated
+            // with respect to JMX 2.0 spec, this method will throw an
+            // IllegalStateException...
+            //
+            if (!Util.isMBeanServerNameUndefined(mbeanServerName)) {
+                delegate.setMBeanServerName(mbeanServerName);
+            }
+
             final MBeanServer mbeanServer =
-                mbsBuilder.newMBeanServer(domain,null,delegate);
+                    mbsBuilder.newMBeanServer(domain,null,delegate);
             if (mbeanServer == null) {
                 final String msg =
-                    "MBeanServerBuilder.newMBeanServer() returned null";
+                        "MBeanServerBuilder.newMBeanServer() returned null";
                 throw new JMRuntimeException(msg);
             }
+
+            // double check that the MBeanServer name is correctly set.
+            // "*" might mean that the caller doesn't have the permission
+            // to see the MBeanServer name.
+            //
+            final String mbsName = Util.getMBeanServerSecurityName(mbeanServer);
+            if (!mbsName.equals(Util.checkServerName(mbeanServerName))
+                && !mbsName.equals("*")) {
+                throw new UnsupportedOperationException(
+                        "can't create MBeanServer with name \""+
+                        mbeanServerName+"\" using "+
+                        builder.getClass().getName());
+            }
+
             return mbeanServer;
         }
     }
@@ -363,7 +586,7 @@
 
         ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
         for (MBeanServer mbs : mBeanServerList) {
-            String name = mBeanServerName(mbs);
+            String name = mBeanServerId(mbs);
             if (agentId.equals(name))
                 result.add(mbs);
         }
@@ -371,8 +594,99 @@
     }
 
     /**
+     * <p>Returns a list of registered MBeanServer objects with the given name.  A
+     * registered MBeanServer object is one that was created by one of
+     * the <code>createMBeanServer</code> or <code>createNamedMBeanServer</code>
+     * methods and not subsequently released with <code>releaseMBeanServer</code>.</p>
+     * <p>See the section about <a href="#MBeanServerName">MBean Server names</a>
+     * above.</p>
+     *
+     * @param mbeanServerName The name of the MBeanServer to
+     * retrieve.  If this parameter is null, all registered MBeanServers
+     * in this JVM are returned.
+     * Otherwise, only those MBeanServers that have a name
+     * matching <code>mbeanServerName</code> are returned: this
+     * parameter can be a pattern, where {@code '*'} matches any
+     * sequence of characters and {@code '?'} matches any character.<br>
+     * The name of an MBeanServer, if specified, is embedded in the
+     * <code>MBeanServerId</code> attribute of its delegate MBean:
+     * this method will parse the <code>MBeanServerId</code> to get the
+     * MBeanServer name. If this parameter is equal to {@code "*"} then
+     * all registered MBeanServers in this JVM are returned, whether they have
+     * a name or not: {@code findMBeanServerByName(null)},
+     * {@code findMBeanServerByName("*")} and {@code findMBeanServer(null)},
+     * are equivalent. It is also possible to get all MBeanServers for which
+     * no name was specified by calling <code>findMBeanServerByName({@value
+     * #DEFAULT_MBEANSERVER_NAME})</code>.
+     *
+     * @return A list of MBeanServer objects.
+     *
+     * @exception SecurityException if there is a SecurityManager and the
+     * caller's permissions do not include or imply <code>{@link
+     * MBeanServerPermission}("findMBeanServer")</code>.
+     *
+     * @see #getMBeanServerName(MBeanServer)
+     * @since 1.7
+     */
+    public synchronized static
+            List<MBeanServer> findMBeanServerByName(String mbeanServerName) {
+
+        checkPermission("findMBeanServer");
+
+        if (mbeanServerName==null || "*".equals(mbeanServerName))
+            return new ArrayList<MBeanServer>(mBeanServerList);
+
+        // noname=true iff we are looking for MBeanServers for which no name
+        // were specified.
+        ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
+        for (MBeanServer mbs : mBeanServerList) {
+            final String name = Util.getMBeanServerSecurityName(mbs);
+            if (Util.wildmatch(name, mbeanServerName)) result.add(mbs);
+        }
+        return result;
+    }
+
+    /**
+     * Returns the name of the MBeanServer embedded in the MBeanServerId of
+     * the given {@code server}. If the given MBeanServerId doesn't contain
+     * any name, {@value #DEFAULT_MBEANSERVER_NAME} is returned.
+     * The MBeanServerId is expected to be of the form:
+     * {@code *[;mbeanServerName=<mbeanServerName>[;*]]}
+     * <br>where {@code *} denotes any sequence of characters, and {@code [ ]}
+     * indicate optional parts.
+     * </p>
+     * <p>For instance, if an MBeanServer is created using {@link
+     * #createNamedMBeanServer(java.lang.String, java.lang.String)
+     * server =
+     * MBeanServerFactory.createNamedMBeanServer("com.mycompany.myapp.server1",
+     * null)} then {@code MBeanServerFactory.getMBeanServerName(server)}
+     * will return {@code "com.mycompany.myapp.server1"} and
+     * <code>server.getAttribute({@link
+     * javax.management.MBeanServerDelegate#DELEGATE_NAME
+     * MBeanServerDelegate.DELEGATE_NAME}, "MBeanServerId")</code> will return
+     * something like
+     * {@code "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"}.
+     * </p>
+     * <p>See the section about <a href="#MBeanServerName">MBean Server names</a>
+     * above.</p>
+     * @param  server A named (or unnamed) MBeanServer.
+     * @return the name of the MBeanServer if found, or
+     *         {@value #DEFAULT_MBEANSERVER_NAME} if no name is
+     *         present in its MBeanServerId, or "*" if its
+     *         MBeanServerId couldn't be obtained. Returning "*" means that
+     *         only {@link MBeanPermission}s that allow all MBean Server names
+     *         will apply to this MBean Server.
+     * @see MBeanServerDelegate
+     * @since 1.7
+     */
+    public static String getMBeanServerName(MBeanServer server) {
+        return Util.getMBeanServerSecurityName(server);
+    }
+
+    /**
      * Return the ClassLoaderRepository used by the given MBeanServer.
-     * This method is equivalent to {@link MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
+     * This method is equivalent to {@link
+     * MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
      * @param server The MBeanServer under examination. Since JMX 1.2,
      * if <code>server</code> is <code>null</code>, the result is a
      * {@link NullPointerException}.  This behavior differs from what
@@ -387,21 +701,23 @@
      *
      **/
     public static ClassLoaderRepository getClassLoaderRepository(
-                                            MBeanServer server) {
+            MBeanServer server) {
         return server.getClassLoaderRepository();
     }
 
-    private static String mBeanServerName(MBeanServer mbs) {
+    private static String mBeanServerId(MBeanServer mbs) {
         try {
             return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
-                                             "MBeanServerId");
+                    "MBeanServerId");
         } catch (JMException e) {
+            JmxProperties.MISC_LOGGER.finest(
+                    "Ignoring exception while getting MBeanServerId: "+e);
             return null;
         }
     }
 
     private static void checkPermission(String action)
-            throws SecurityException {
+    throws SecurityException {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             Permission perm = new MBeanServerPermission(action);
@@ -425,16 +741,16 @@
     }
 
     private static final ArrayList<MBeanServer> mBeanServerList =
-        new ArrayList<MBeanServer>();
+            new ArrayList<MBeanServer>();
 
     /**
      * Load the builder class through the context class loader.
      * @param builderClassName The name of the builder class.
      **/
     private static Class loadBuilderClass(String builderClassName)
-            throws ClassNotFoundException {
+    throws ClassNotFoundException {
         final ClassLoader loader =
-            Thread.currentThread().getContextClassLoader();
+                Thread.currentThread().getContextClassLoader();
 
         if (loader != null) {
             // Try with context class loader
@@ -453,14 +769,14 @@
      **/
     private static MBeanServerBuilder newBuilder(Class builderClass) {
         try {
-            final Object builder = builderClass.newInstance();
-            return (MBeanServerBuilder)builder;
+            final Object abuilder = builderClass.newInstance();
+            return (MBeanServerBuilder)abuilder;
         } catch (RuntimeException x) {
             throw x;
         } catch (Exception x) {
             final String msg =
-               "Failed to instantiate a MBeanServerBuilder from " +
-               builderClass + ": " + x;
+                    "Failed to instantiate a MBeanServerBuilder from " +
+                    builderClass + ": " + x;
             throw new JMRuntimeException(msg, x);
         }
     }
@@ -472,7 +788,7 @@
     private static synchronized void checkMBeanServerBuilder() {
         try {
             GetPropertyAction act =
-                new GetPropertyAction(JMX_INITIAL_BUILDER);
+                    new GetPropertyAction(JMX_INITIAL_BUILDER);
             String builderClassName = AccessController.doPrivileged(act);
 
             try {
@@ -493,8 +809,8 @@
                 builder = newBuilder(newBuilderClass);
             } catch (ClassNotFoundException x) {
                 final String msg =
-                    "Failed to load MBeanServerBuilder class " +
-                    builderClassName + ": " + x;
+                        "Failed to load MBeanServerBuilder class " +
+                        builderClassName + ": " + x;
                 throw new JMRuntimeException(msg, x);
             }
         } catch (RuntimeException x) {
--- a/src/share/classes/javax/management/ObjectName.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/ObjectName.java	Thu Sep 04 14:46:36 2008 +0200
@@ -27,6 +27,8 @@
 
 import com.sun.jmx.mbeanserver.GetPropertyAction;
 import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.namespace.serial.JMXNamespaceContext;
+
 import java.io.IOException;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
@@ -223,6 +225,17 @@
 public class ObjectName implements Comparable<ObjectName>, QueryExp {
 
     /**
+     * The sequence of characters used to separate name spaces in a name space
+     * path.
+     *
+     * @see javax.management.namespace
+     * @since 1.7
+     **/
+    public static final String NAMESPACE_SEPARATOR = "//";
+    private static final int NAMESPACE_SEPARATOR_LENGTH =
+            NAMESPACE_SEPARATOR.length();
+
+    /**
      * A structure recording property structure and
      * proposing minimal services
      */
@@ -251,16 +264,17 @@
         /**
          * Returns a key string for receiver key
          */
-        String getKeyString(String name) {
-            return name.substring(_key_index, _key_index + _key_length);
+        String getKeyString(String name, int offset) {
+            final int start = _key_index+offset;
+            return name.substring(start, start + _key_length);
         }
 
         /**
          * Returns a value string for receiver key
          */
-        String getValueString(String name) {
-            int in_begin = _key_index + _key_length + 1;
-            int out_end = in_begin + _value_length;
+        String getValueString(String name, int offset) {
+            final int in_begin = _key_index + offset + _key_length + 1;
+            final int out_end = in_begin + _value_length;
             return name.substring(in_begin, out_end);
         }
     }
@@ -393,6 +407,45 @@
      */
     private transient boolean _property_value_pattern = false;
 
+    private ObjectName(String newDomain, ObjectName aname)
+        throws MalformedObjectNameException{
+        copyToOtherDomain(newDomain,aname);
+    }
+
+    private void copyToOtherDomain(String domain, ObjectName aname)
+        throws MalformedObjectNameException, NullPointerException {
+
+        // The domain cannot be null
+        if (domain == null)
+            throw new NullPointerException("domain cannot be null");
+
+        // The key property list cannot be null
+        if (aname == null)
+            throw new MalformedObjectNameException(
+                        "key property list cannot be empty");
+
+        // checks domain validity. A side effect of this method is also to
+        // set the _domain_pattern flag.
+        if (!isDomain(domain))
+            throw new MalformedObjectNameException("Invalid domain: " + domain);
+
+        // init canonicalname
+        _domain_length = domain.length();
+
+        _canonicalName = (domain +
+             aname._canonicalName.substring(aname._domain_length)).intern();
+        _kp_array = aname._kp_array;
+        _ca_array = aname._ca_array;
+        _propertyList = aname._propertyList;
+        _property_list_pattern = aname._property_list_pattern;
+        _property_value_pattern = aname._property_value_pattern;
+        // TODO remove this hack
+        // if (toString().endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) {
+        //    Thread.currentThread().dumpStack();
+        //    throw new Error("************************ Gotcha!");
+        //}
+    }
+
     // Instance private fields <=======================================
 
     // Private fields <========================================
@@ -435,10 +488,10 @@
         }
 
         // initialize parsing of the string
-        char[] name_chars = name.toCharArray();
-        int len = name_chars.length;
-        char[] canonical_chars = new char[len]; // canonical form will be same
-                                                // length at most
+        final char[] name_chars = name.toCharArray();
+        final int len = name_chars.length;
+        final char[] canonical_chars = new char[len]; // canonical form will
+                                                      // be same length at most
         int cname_index = 0;
         int index = 0;
         char c, c1;
@@ -637,10 +690,12 @@
 
             // we got the key and value part, prepare a property for this
             if (!value_pattern) {
-                prop = new Property(key_index, key_length, value_length);
+                prop = new Property(key_index-_domain_length,
+                                    key_length, value_length);
             } else {
                 _property_value_pattern = true;
-                prop = new PatternProperty(key_index, key_length, value_length);
+                prop = new PatternProperty(key_index-_domain_length,
+                                    key_length, value_length);
             }
             key_name = name.substring(key_index, key_index + key_length);
 
@@ -725,12 +780,12 @@
             boolean value_pattern = checkValue(value);
             sb.append(value);
             if (!value_pattern) {
-                prop = new Property(key_index,
+                prop = new Property(key_index-_domain_length,
                                     key.length(),
                                     value.length());
             } else {
                 _property_value_pattern = true;
-                prop = new PatternProperty(key_index,
+                prop = new PatternProperty(key_index-_domain_length,
                                            key.length(),
                                            value.length());
             }
@@ -810,9 +865,9 @@
                 prop = _ca_array[i];
                 // length of prop including '=' char
                 prop_len = prop._key_length + prop._value_length + 1;
-                System.arraycopy(specified_chars, prop._key_index,
+                System.arraycopy(specified_chars, prop._key_index+_domain_length,
                                  canonical_chars, prop_index, prop_len);
-                prop.setKeyIndex(prop_index);
+                prop.setKeyIndex(prop_index-_domain_length);
                 prop_index += prop_len;
                 if (i != last_index) {
                     canonical_chars[prop_index] = ',';
@@ -1031,33 +1086,6 @@
                                          k[endKey] + "'");
     }
 
-    /*
-     * Tests whether string s is matched by pattern p.
-     * Supports "?", "*" each of which may be escaped with "\";
-     * Not yet supported: internationalization; "\" inside brackets.<P>
-     * Wildcard matching routine by Karl Heuer.  Public Domain.<P>
-     */
-    private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
-        char c;
-        final int slen = s.length;
-        final int plen = p.length;
-
-        while (pi < plen) { // While still string
-            c = p[pi++];
-            if (c == '?') {
-                if (++si > slen) return false;
-            } else if (c == '*') { // Wildcard
-                if (pi >= plen) return true;
-                do {
-                    if (wildmatch(s,p,si,pi)) return true;
-                } while (++si < slen);
-                return false;
-            } else {
-                if (si >= slen || c != s[si++]) return false;
-            }
-        }
-        return (si == slen);
-    }
 
     // Category : Internal utilities <==============================
 
@@ -1177,15 +1205,43 @@
             cn = (String)in.readObject();
         }
 
+        final JMXNamespaceContext ctxt =
+                JMXNamespaceContext.getDeserializationContext();
         try {
-            construct(cn);
+            construct(changeContext(ctxt,cn));
         } catch (NullPointerException e) {
             throw new InvalidObjectException(e.toString());
+        } catch (IllegalArgumentException e) {
+            throw new InvalidObjectException(e.toString());
         } catch (MalformedObjectNameException e) {
             throw new InvalidObjectException(e.toString());
         }
     }
 
+    private String changeContext(JMXNamespaceContext context, String nameString) {
+        final String old = context.prefixToRemove;
+        final String nw  = context.prefixToAdd;
+        final int ol = old.length();
+        if (nameString.startsWith(NAMESPACE_SEPARATOR)) return nameString;
+        if (ol>0) {
+            if (!nameString.startsWith(old) ||
+                    !nameString.startsWith(NAMESPACE_SEPARATOR,ol))
+                throw new IllegalArgumentException(
+                        "Serialized ObjectName does not start with " + old +
+                        ": " + nameString);
+            nameString = nameString.substring(ol+NAMESPACE_SEPARATOR_LENGTH);
+        }
+        if (!nw.equals("")) {
+            nameString = nw + NAMESPACE_SEPARATOR + nameString;
+        }
+        // TODO remove this hack
+        // if (nameString.endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) {
+        //    System.err.println("old="+old+", nw="+nw);
+        //    Thread.currentThread().dumpStack();
+        //    throw new Error("************************ Gotcha!");
+        // }
+        return nameString;
+    }
 
     /**
      * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
@@ -1248,15 +1304,22 @@
     private void writeObject(ObjectOutputStream out)
             throws IOException {
 
+      final JMXNamespaceContext ctxt =
+                JMXNamespaceContext.getSerializationContext();
+
       if (compat)
       {
         // Serializes this instance in the old serial form
         // Read CR 6441274 before making any changes to this code
         ObjectOutputStream.PutField fields = out.putFields();
-        fields.put("domain", _canonicalName.substring(0, _domain_length));
+        final String domain =
+                changeContext(ctxt,_canonicalName.substring(0, _domain_length));
+        final String cn =
+                changeContext(ctxt,_canonicalName);
+        fields.put("domain", domain);
         fields.put("propertyList", getKeyPropertyList());
         fields.put("propertyListString", getKeyPropertyListString());
-        fields.put("canonicalName", _canonicalName);
+        fields.put("canonicalName", cn);
         fields.put("pattern", (_domain_pattern || _property_list_pattern));
         fields.put("propertyPattern", _property_list_pattern);
         out.writeFields();
@@ -1266,7 +1329,8 @@
         // Serializes this instance in the new serial form
         //
         out.defaultWriteObject();
-        out.writeObject(getSerializedNameString());
+
+        out.writeObject(changeContext(ctxt,getSerializedNameString()));
       }
     }
 
@@ -1397,6 +1461,27 @@
     }
 
     /**
+     * Returns an {@code ObjectName} that is the same as this one but
+     * with the specified domain.
+     * This method preserves the original key order in the new instance.
+     * If the provided name has a key property pattern, it will also be
+     * preserved in the returned instance.
+     *
+     * @param newDomain The new domain for the returned instance;
+     *        must not be null.
+     * @return A new {@code ObjectName} that is the same as {@code this}
+     *         except the domain is {@code newDomain}.
+     * @throws NullPointerException if {@code newDomain} is null.
+     * @throws MalformedObjectNameException if the new domain is syntactically
+     *         illegal.
+     * @since 1.7
+     **/
+    public final ObjectName withDomain(String newDomain)
+            throws NullPointerException, MalformedObjectNameException {
+        return new ObjectName(newDomain, this);
+    }
+
+    /**
      * Construct an object name from the given string.
      *
      * @param name  A string representation of the object name.
@@ -1550,7 +1635,7 @@
             throw new NullPointerException("key property can't be null");
         for (int i = 0; i < _ca_array.length; i++) {
             Property prop = _ca_array[i];
-            String key = prop.getKeyString(_canonicalName);
+            String key = prop.getKeyString(_canonicalName,_domain_length);
             if (key.equals(property))
                 return (prop instanceof PatternProperty);
         }
@@ -1630,8 +1715,10 @@
                 Property prop;
                 for (int i = len - 1; i >= 0; i--) {
                     prop = _ca_array[i];
-                    _propertyList.put(prop.getKeyString(_canonicalName),
-                                      prop.getValueString(_canonicalName));
+                    _propertyList.put(prop.getKeyString(_canonicalName,
+                                            _domain_length),
+                                      prop.getValueString(_canonicalName,
+                                            _domain_length));
                 }
             }
         }
@@ -1716,7 +1803,8 @@
             }
         }
 
-        return new String(dest_chars);
+        final String name = new String(dest_chars);
+        return name;
     }
 
     /**
@@ -1734,7 +1822,7 @@
         if (_kp_array.length == 0) return offset;
 
         final char[] dest_chars = data;
-        final char[] value = _canonicalName.toCharArray();
+        final char[] value = canonicalChars;
 
         int index = offset;
         final int len = _kp_array.length;
@@ -1742,7 +1830,7 @@
         for (int i = 0; i < len; i++) {
             final Property prop = _kp_array[i];
             final int prop_len = prop._key_length + prop._value_length + 1;
-            System.arraycopy(value, prop._key_index, dest_chars, index,
+            System.arraycopy(value, prop._key_index+_domain_length, dest_chars, index,
                              prop_len);
             index += prop_len;
             if (i < last ) dest_chars[index++] = ',';
@@ -1816,7 +1904,7 @@
         // (because usage of intern())
         ObjectName on = (ObjectName) object;
         String on_string = on._canonicalName;
-        if (_canonicalName == on_string) return true;
+        if (_canonicalName == on_string) return true;  // ES: OK
 
         // Because we are sharing canonical form between object names,
         // we have finished the comparison at this stage ==> unequal
@@ -1997,9 +2085,9 @@
     private final boolean matchDomains(ObjectName name) {
         if (_domain_pattern) {
             // wildmatch domains
-            final char[] dom_pattern = getDomain().toCharArray();
-            final char[] dom_string  = name.getDomain().toCharArray();
-            return wildmatch(dom_string,dom_pattern,0,0);
+            // This ObjectName is the pattern
+            // The other ObjectName is the string.
+            return Util.wildpathmatch(name.getDomain(),getDomain());
         }
         return getDomain().equals(name.getDomain());
     }
@@ -2025,7 +2113,7 @@
                 // index in receiver
                 //
                 final Property p = props[i];
-                final String   k = p.getKeyString(cn);
+                final String   k = p.getKeyString(cn,_domain_length);
                 final String   v = nameProps.get(k);
                 // Did we find a value for this key ?
                 //
@@ -2034,15 +2122,13 @@
                 //
                 if (_property_value_pattern && (p instanceof PatternProperty)) {
                     // wildmatch key property values
-                    final char[] val_pattern =
-                            p.getValueString(cn).toCharArray();
-                    final char[] val_string  = v.toCharArray();
-                    if (wildmatch(val_string,val_pattern,0,0))
+                    // p is the property pattern, v is the string
+                    if (Util.wildmatch(v,p.getValueString(cn,_domain_length)))
                         continue;
                     else
                         return false;
                 }
-                if (v.equals(p.getValueString(cn))) continue;
+                if (v.equals(p.getValueString(cn,_domain_length))) continue;
                 return false;
             }
             return true;
@@ -2109,6 +2195,10 @@
      * @since 1.6
      */
     public int compareTo(ObjectName name) {
+        // Quick optimization:
+        //
+        if (name == this) return 0;
+
         // (1) Compare domains
         //
         int domainValue = this.getDomain().compareTo(name.getDomain());
--- a/src/share/classes/javax/management/event/EventClient.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/event/EventClient.java	Thu Sep 04 14:46:36 2008 +0200
@@ -29,6 +29,7 @@
 import com.sun.jmx.event.LeaseRenewer;
 import com.sun.jmx.event.ReceiverBuffer;
 import com.sun.jmx.event.RepeatedSingletonJob;
+import com.sun.jmx.namespace.JMXNamespaceUtils;
 import com.sun.jmx.mbeanserver.PerThreadGroupPool;
 import com.sun.jmx.remote.util.ClassLogger;
 
@@ -1063,6 +1064,24 @@
         return clientId;
     }
 
+    /**
+     * Returns a JMX Connector that will use an {@link EventClient}
+     * to subscribe for notifications. If the server doesn't have
+     * an {@link EventClientDelegateMBean}, then the connector will
+     * use the legacy notification mechanism instead.
+     *
+     * @param wrapped The underlying JMX Connector wrapped by the returned
+     *               connector.
+     *
+     * @return A JMX Connector that will uses an {@link EventClient}, if
+     *         available.
+     *
+     * @see EventClient#getEventClientConnection(MBeanServerConnection)
+     */
+    public static JMXConnector withEventClient(final JMXConnector wrapped) {
+        return JMXNamespaceUtils.withEventClient(wrapped);
+    }
+
     private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
             leaseRenewerThreadPool = PerThreadGroupPool.make();
 }
--- a/src/share/classes/javax/management/event/EventClientDelegate.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/event/EventClientDelegate.java	Thu Sep 04 14:46:36 2008 +0200
@@ -721,7 +721,10 @@
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             try {
-                ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged(
+                final String serverName = getMBeanServerName();
+
+                ObjectInstance oi = (ObjectInstance)
+                    AccessController.doPrivileged(
                         new PrivilegedExceptionAction<Object>() {
                     public Object run()
                     throws InstanceNotFoundException {
@@ -731,6 +734,7 @@
 
                 String classname = oi.getClassName();
                 MBeanPermission perm = new MBeanPermission(
+                        serverName,
                         classname,
                         null,
                         name,
@@ -746,6 +750,20 @@
         return true;
     }
 
+    private String getMBeanServerName() {
+        if (mbeanServerName != null) return mbeanServerName;
+        else return (mbeanServerName = getMBeanServerName(mbeanServer));
+    }
+
+    private static String getMBeanServerName(final MBeanServer server) {
+        final PrivilegedAction<String> action = new PrivilegedAction<String>() {
+            public String run() {
+                return Util.getMBeanServerSecurityName(server);
+            }
+        };
+        return AccessController.doPrivileged(action);
+    }
+
     // ------------------------------------
     // private variables
     // ------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXNamespace.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,660 @@
+/*
+ * 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.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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}.
+ * </p>
+ * <p>
+ * You can create a local namespace by supplying a newly created MBean Server
+ * to an instance of {@code JMXNamespace}. For instance:
+ * <pre>
+ * final String namespace = "foo";
+ * final ObjectName namespaceName = {@link JMXNamespaces#getNamespaceObjectName
+ *       JMXNamespaces.getNamespaceObjectName(namespace)};
+ * server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
+ *                      namespaceName);
+ * </pre>
+ * </p>
+ * <p>
+ * <u>Note:</u> 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.
+ * </p>
+ *
+ * <p>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:
+ * </p>
+ * <pre>
+ * 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));
+ * </pre>
+ *
+ * <h2 id="PermissionChecks">JMX Namespace Permission Checks</h2>
+ *
+ * <p>A special {@link JMXNamespacePermission} is defined to check access
+ * to MBean within namespaces.</p>
+ * <p>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=<MBeanType>} key
+ * in their object name.
+ * <p>
+ * Another difference is that {@link JMXNamespacePermission
+ * JMXNamespacePermission} also specifies from which namespace and which
+ * MBean server the permission is granted.
+ * </p>
+ * <p>In the rest of this document, the following terms are used:</p>
+ * <ul>
+ *    <li id="MBeanServerName"><p>{@code server name} is the
+ *    <a href="../MBeanServerFactory.html#MBeanServerName">name of the
+ *    MBeanServer</a> in which the permission is granted.
+ *    The name of an {@code MBeanServer} can be obtained by calling {@link
+ *    javax.management.MBeanServerFactory#getMBeanServerName
+ *    MBeanServerFactory.getMBeanServerName(mbeanServer)}
+ *    </p>
+ *    <li id="NamespaceName"><p>{@code namespace} is the name of the namespace
+ *    in the <a href="#MBeanServerName">named MBean server</a> for which the
+ *    permission is granted. It doesn't contain any
+ *    {@link JMXNamespaces#NAMESPACE_SEPARATOR namespace separator}.
+ *    </p>
+ *    <li id="MBeanName"><p>{@code mbean} is the name
+ *    of the MBean in that {@code namespace}. This is the name of the MBean
+ *    in the namespace's {@link JMXNamespace#getSourceServer() source mbean server}.
+ *    It might contain no, one, or several {@link
+ *    JMXNamespaces#NAMESPACE_SEPARATOR namespace separators}.
+ *    </p>
+ * </ul>
+ *
+ * <p>For instance let's assume that some piece of code calls:</p>
+ * <pre>
+ *     final MBeanServer mbeanServer = ...;
+ *     final ObjectName  name   = new ObjectName("a//b//c//D:k=v");
+ *     mbeanServer.getAttribute(name,"Foo");
+ * </pre>
+ * <p>
+ *   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:
+ * </p>
+ * <ol>
+ * <li id="check1">
+ * <code>JMXNamespacePermission(mbeanServerName, "Foo", "<b>a//</b>b//c//D:k=v",
+ * "getAttribute")</code>
+ * (where {@code mbeanServerName=MBeanServerFactory.getMBeanServerName(mbeanServer)},
+ * <code>namespace="<b>a</b>"</code>, and {@code mbean="b//c//D:k=v"})
+ * </li>
+ * <li id="check2">and in addition if namespace {@code "a"} is local,
+ * <code>JMXNamespacePermission(aSourceServerName,"Foo","<b>b//</b>c//D:k=v",
+ *       "getAttribute")}</code>
+ * (where
+ * {@code aSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(a))},
+ * <code>namespace="<b>b</b>"</code>, and {@code mbean="c//D:k=v"}),
+ * </li>
+ * <li id="check3">and in addition if namespace {@code "b"} is also local,
+ * <code>JMXNamespacePermission(bSourceServerName,"Foo","<b>c//</b>D:k=v",
+ *       "getAttribute")}</code>
+ * (where
+ * {@code bSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(b))},
+ * <code>namespace="<b>c</b>"</code>, and {@code mbean="D:k=v"}),
+ * </li>
+ * <li id="check4">and in addition if the source mbean server of namespace
+ * {@code "c"} is a also a local MBeanServer in this JVM,
+ * {@code MBeanPermission(cSourceServerName,<className(D:k=v)>,"Foo","D:k=v","getAttrinute")},
+ * (where
+ * {@code cSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(c))}).
+ * </li>
+ * </ol>
+ * <p>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}.
+ * </p>
+ * <p>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 <a href="#check2">2</a>,
+ *    <a href="#check3">3</a>, and <a href="#check4">4</a> 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}.
+ * </p>
+ * <p>In all cases, {@linkplain JMXNamespacePermission JMX Namespace Permissions}
+ * are checked as follows:</p>
+ * <p>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.</p>
+ *
+ * <p>Assuming that there is a security manager, or that the
+ * implementation chooses to make checks anyway, the checks are made
+ * as detailed below.</p>
+ *
+ * <p>If a security check fails, the method throws {@link
+ * SecurityException}.</p>
+ *
+ * <ul>
+ *
+ * <li><p>For the {@link MBeanServer#invoke invoke} method, the caller's
+ * permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;operation name&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "invoke")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#getAttribute getAttribute} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;attribute&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")}.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#getAttributes getAttributes} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;null&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * Additionally, for each attribute <em>att</em> in the {@link
+ * javax.management.AttributeList}, if the caller's permissions do not
+ * imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, <em>att</em>,
+ * &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")}, the
+ * MBean server will behave as if that attribute had not been in the
+ * supplied list.</p>
+ *
+ * <li><p>For the {@link MBeanServer#setAttribute setAttribute} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;attrName&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace, and
+ * <code>attrName</code> is {@link javax.management.Attribute#getName()
+ * attribute.getName()}.</p>
+ *
+ * <li><p>For the {@link MBeanServer#setAttributes setAttributes} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * Additionally, for each attribute <em>att</em> in the {@link
+ * javax.management.AttributeList}, if the caller's permissions do not
+ * imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, <em>att</em>, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
+ * the MBean server will behave as if that attribute had not been in the
+ * supplied list.</p>
+ *
+ * <li><p>For the <code>addNotificationListener</code> methods,
+ * the caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "addNotificationListener")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the <code>removeNotificationListener</code> methods,
+ * the caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "removeNotificationListener")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#getMBeanInfo getMBeanInfo} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "getMBeanInfo")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#getObjectInstance getObjectInstance} method,
+ * the caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "getObjectInstance")},
+ * where <a href="#MBeanServerName">mbean server name/a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#isInstanceOf isInstanceOf} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "isInstanceOf")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#queryMBeans queryMBeans} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, null,
+ * "queryMBeans")}.
+ * Additionally, for each MBean {@code mbean} that matches {@code pattern},
+ * if the caller's permissions do not imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "queryMBeans")}, the
+ * MBean server will behave as if that MBean did not exist.</p>
+ *
+ * <p>Certain query elements perform operations on the MBean server.
+ * However these operations are usually performed by the MBeanServer at the
+ * bottom of the namespace path, and therefore, do not involve any
+ * {@link JMXNamespacePermission} permission check. They might involve
+ * {@link javax.management.MBeanPermission} checks depending on how security
+ * in the JVM in which the bottom MBeanServer resides is implemented.
+ * See {@link javax.management.MBeanServer} for more details.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#queryNames queryNames} method, the checks
+ * are the same as for <code>queryMBeans</code> except that
+ * <code>"queryNames"</code> is used instead of
+ * <code>"queryMBeans"</code> in the <code>JMXNamespacePermission</code>
+ * objects.  Note that a <code>"queryMBeans"</code> permission implies
+ * the corresponding <code>"queryNames"</code> permission.</p>
+ *
+ * <li><p>For the {@link MBeanServer#getClassLoader getClassLoader} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;loaderName&gt;,
+ * "getClassLoader")},
+ * where <a href="#MBeanServerName">mbean server name/a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">loaderName</a> is the name of the ClassLoader MBean
+ * which is accessed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#getClassLoaderFor getClassLoaderFor} method,
+ * the caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "getClassLoaderFor")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
+ * is performed, in that namespace.
+ * </p>
+ *
+ * <li><p>For the {@link MBeanServer#registerMBean registerMBean} method, the
+ * caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "registerMBean")}.  Here
+ * <code>class name</code> is the string returned by {@code
+ * obj.getClass().getName()} where {@code obj} is the mbean reference,
+ * <a href="#MBeanServerName"mbean server name/a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean which is being
+ * registered, relative to that namespace.
+ *
+ * <li><p>For the <code>createMBean</code> methods, the caller's
+ * permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "instantiate")} and
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "registerMBean")}, where
+ * <code>class name</code> is the string passed as first argument to the {@code
+ * createMBean} method,
+ * <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean which is being
+ * created, relative to that namespace.
+ *
+ * <li><p>For the {@link MBeanServer#unregisterMBean unregisterMBean} method,
+ * the caller's permissions must imply {@link
+ * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
+ * JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
+ * "unregisterMBean")},
+ * where <a href="#MBeanServerName">mbean server name</a> is the name of the
+ * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
+ * <a href="#NamespaceName">namespace</a> is registered, and
+ * <a href="#MBeanName">mbean</a> is the name of the MBean on which is
+ * being unregistered, relative to that namespace.
+ * </p>
+ * </ul>
+ * </p>
+ * <p>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:
+ * </p>
+ * <pre>
+ *     permission javax.management.namespace.JMXNamespacePermission "*::*[]", "*";
+ * </pre>
+ *
+ * @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.<br>
+     * 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.<br>
+     * This is
+     * <code>{@value #TYPE_ASSIGNMENT}</code>.
+     **/
+    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.
+     * <p>
+     * This method is called by the MBean server.
+     * Application classes should never call this method directly.
+     * <p>
+     * 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. <var>name</var> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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 <a href="#PermissionChecks">JMX Namespace Permission Checks</a>
+     * 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()}.
+    * <br>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()}.
+    * <br>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()}.
+    * <br>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;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXNamespaceMBean.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+/**
+ * A {@link JMXNamespace} is an MBean that handles a name space in the
+ * MBeanServer hierarchical name space.
+ * @see JMXNamespace
+ * @since 1.7
+ */
+public interface JMXNamespaceMBean {
+
+    // Note: since getDomains(), getDefaultDomain(), and getMBeanCount()
+    // don't take any ObjectName parameters, the only efficient way
+    // to get these data is to call the corresponding method on the
+    // JMXNamespaceMBean that handles the name space.
+    //
+    // We need these methods to implement 'cd' (JMXNamespaces.narrowToNamespace)
+    // correctly.
+    //
+
+    /**
+     * Returns the list of domains currently implemented in the name space
+     * handled by this {@link JMXNamespace}.
+     * @throws IOException if the domain list cannot be obtained due to
+     *         I/O problems (communication failures etc...).
+     * @return the list of domains currently implemented in the name space
+     * handled by this {@link JMXNamespace}.
+     * @see javax.management.MBeanServerConnection#getDomains
+     *      MBeanServerConnection.getDomains
+     **/
+    public String[] getDomains() throws IOException;
+
+    /**
+     * Returns the default domain for the name space handled by
+     * this {@link JMXNamespace}.
+     * @throws IOException if the default domain cannot be obtained due to
+     *         I/O problems (communication failures etc...).
+     * @return the default domain for the name space handled by
+     * this {@link JMXNamespace}.
+     * @see javax.management.MBeanServerConnection#getDefaultDomain
+     *      MBeanServerConnection.getDefaultDomain
+     **/
+    public String   getDefaultDomain() throws IOException;
+
+    /**
+     * Returns the number of MBeans registered in the  name space handled by
+     *         this  {@link JMXNamespace}.
+     *
+     * @return the number of MBeans registered in the  name space handled by
+     *         this  {@link JMXNamespace}.
+     *
+     * @throws IOException if the MBean count cannot be obtained due to
+     *         I/O problems (communication failures etc...).
+     * @see javax.management.MBeanServerConnection#getMBeanCount
+     *      MBeanServerConnection.getMBeanCount
+     */
+    public Integer  getMBeanCount() throws IOException;
+
+    /**
+     * Returns a {@link java.util.UUID UUID string} which uniquely identifies
+     * this {@linkplain JMXNamespace} MBean.
+     * This information can be used to detect loops in the JMX name space graph.
+     * @return A unique ID identifying this MBean.
+     * @throws IOException if the MBean UUID cannot be obtained due to
+     *         I/O problems (communication failures etc...).
+     */
+    public String getUUID() throws IOException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXNamespacePermission.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,1474 @@
+/*
+ * Copyright 2002-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 javax.management.*;
+import com.sun.jmx.mbeanserver.Util;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.security.Permission;
+
+/**
+ * <p>A permission controlling access to MBeans located in namespaces.
+ * If a security manager has been set using {@link
+ * System#setSecurityManager}, most operations on an MBean mounted in a
+ * namespace require that the caller's permissions imply a
+ * JMXNamespacePermission appropriate for the operation.
+ * This is described in detail in the
+ * documentation for the
+ * <a href="JMXNamespace.html#PermissionChecks">JMXNamespace</a>
+ * class.</p>
+ *
+ * <p>As with other {@link Permission} objects,
+ * a JMXNamespacePermission can represent either a permission that
+ * you <em>have</em> or a permission that you <em>need</em>.
+ * When a sensitive operation is being checked for permission,
+ * a JMXNamespacePermission is constructed
+ * representing the permission you need.  The operation is only
+ * allowed if the permissions you have {@linkplain #implies imply} the
+ * permission you need.</p>
+ *
+ * <p>A JMXNamespacePermission contains four items of information:</p>
+ *
+ * <ul>
+ *
+ * <li id="Action"><p>The <em>action</em>.</p>
+ * <p>For a permission you need,
+ * this is one of the actions in the list <a
+ * href="#action-list">below</a>.  For a permission you have, this is
+ * a comma-separated list of those actions, or <code>*</code>,
+ * representing all actions.</p>
+ *
+ * <p>The action is returned by {@link #getActions()}.</p>
+ *
+ * <li id="MBeanServerName"><p>The <em>MBean Server name</em>.</p>
+ *
+ * <p>For a permission you need, this is the {@linkplain
+ * javax.management.MBeanServerFactory#getMBeanServerName
+ * name of the MBeanServer}
+ * from which the <a href="#MBeanName">MBean</a> is accessed.</p>
+ *
+ * <p>For a permission you have, this is either the  {@linkplain
+ * javax.management.MBeanServerFactory#getMBeanServerName
+ * name of the MBeanServer} from which the <a href="#MBeanName">MBean</a>
+ * for which you have this permission is accessed,
+ * or a pattern against which that MBean Server name will be matched.<br>
+ * An {@code mbeanServername} pattern can also be empty, or the single
+ * character {@code "*"}, both of which match any {@code MBeanServer} name.
+ * The string {@code "-"} doesn't match any MBeanServer name.
+ * </p>
+ *
+ * <p>Example:</p>
+ * <pre>
+ *   // grant permission to invoke the operation "stop" on any MBean
+ *   // whose name matches "a//*&#42;//*:type=JMXConnectorServer" when
+ *   // accessed from any MBeanServer whose name matches myapp.*"
+ *   permission javax.management.namespace.JMXNamespacePermission "myapp.*::stop[a//*&#42;//*:type=JMXConnectorServer]", "invoke";
+ * </pre>
+ *
+ * <li id="Member"><p>The <em>member</em>.</p>
+ *
+ * <p>For a permission you need, this is the name of the attribute or
+ * operation you are accessing.  For operations that do not reference
+ * an attribute or operation, the member is null.</p>
+ *
+ * <p>For a permission you have, this is either the name of an attribute
+ * or operation you can access, or it is empty or the single character
+ * "<code>*</code>", both of which grant access to any member.</p>
+ *
+ * <p>There is a special case for actions {@code registerMBean} and
+ *    {@code instantiate}, where for a permission you need, {@code member}
+ *    indicates the name of the class for which you are trying
+ *    to create, instantiate, or register an MBean instance. For a
+ *    permission you have, it is a pattern that will be matched against
+ *    the full class name of the MBean being created, instantiated, or
+ *    registered.
+ * </p>
+ *
+ *
+ * <li id="MBeanName"><p>The <em>object name</em>.</p>
+ *
+ * <p>For a permission you need, this is the {@link ObjectName} of the
+ * MBean you are accessing. It is of the form {@code <namespace>//<mbean name>}
+ * where {@code <namespace>} is the name of the name space for which the
+ * permission is checked, and {@code <mbean name>} is the name of the MBean
+ * within that namespace.
+ * <br>
+ * For operations that do not reference a
+ * single MBean, the <em>object name</em> is null.  It is never an object
+ * name pattern.
+ * </p>
+ *
+ * <p>For a permission you have, this is the {@link ObjectName} of the
+ * MBean or MBeans you can access. It is of the form
+ * {@code <namespace>//<mbean name>}
+ * where {@code <namespace>} is the name of the name space for which the
+ * permission is checked, and
+ * {@code <mbean name>} is the name of the MBean
+ * within that namespace. Both {@code <namespace>} and {@code <mbean name>}
+ * can be patterns. The <em>object name</em>
+ * may also be empty, which grants access to all MBeans whatever their
+ * name and namespace.
+ * When included in a namespace path the special path element
+ * <code>**</code> matches any number of sub namespaces
+ * recursively, but only if used as a complete namespace path element,
+ * as in <code>*&#42;//b//c//D:k=v</code> or <code>a//*&#42;//c//D:k=v</code>
+ * - see <a href="#metawildcard">below</a>.
+ * </p>
+ *
+ *
+ * </ul>
+ *
+ * <p>If you have a JMXNamespacePermission, it allows operations only
+ * if all four of the items match.</p>
+ *
+ * <p>The <a href="#MBeanServerName">MBeanServer name</a>,
+ * <a href="#Member">member</a>, and <a href="#MBeanName">object name</a>
+ * can be written together
+ * as a single string, which is the <em>name</em> of this permission.
+ * The name of the permission is the string returned by {@link
+ * java.security.Permission#getName() getName()}.
+ * The format of the string is:</p>
+ *
+ * <blockquote>
+ * {@code <mbean server name>::<member>[<namespace>//<mbean name>]}
+ * </blockquote>
+ *
+ * <p>
+ * The {@code <mbean server name>} is optional. If omitted, {@code "*"} is
+ * assumed, and these three permission names
+ * are thus equivalent:
+ * </p>
+ * <blockquote>
+ * {@code *::<member>[<namespace>//<mbean name>]}<br>
+ * {@code ::<member>[<namespace>//<mbean name>]}<br>
+ * {@code <member>[<namespace>//<mbean name>]}<br>
+ * </blockquote>
+ * <p>
+ *    The {@code <namespace>//<mbean name>} string can be in the form
+ *    of a traditional ObjectName
+ *    pattern - meaning that <code>?</code> will match any single
+ *    character, and <code>*</code> will match any sequence of characters,
+ *    except {@value
+ *    javax.management.namespace.JMXNamespaces#NAMESPACE_SEPARATOR}
+ *    In addition, when included in a namespace path the special
+ *    path element <code>**</code> matches any number of sub namespaces
+ *    recursively.
+ *    A {@code <namespace>//<mbean name>} string of the form
+ *    <code>*&#42;//*:*</code> thus means that the permission is
+ *    granted for all MBeans in all namespaces, recursively (see
+ *    <a href="#metawildcard">below</a> for more details.
+ * </p>
+ * <p>Namespace permission checking may be tricky to configure, depending
+ *    on whether the namespaces crossed to reach the MBean are local or
+ *    remote.<br>
+ *    For instance, let <code>a//b//D:k=v</code> be an MBean exposing an
+ *    attribute <code>Foo</code>.
+ *    If namespace <code>a</code> is a plain JMXNamespace pointing to
+ *    a local MBeanServer in the same JVM, then the permissions you need
+ *    to get the attribute <code>Foo</code> will be:
+ * </p>
+ * <pre>
+ *    // granting permission to access attribute 'Foo' of MBean a//b//D:k=v
+ *    // from MBeanServer named 'srv1'
+ *    // This permission will be checked by the MBeanServer that contains 'a'.
+ *    srv1::Foo[a//b//D:k=v]
+ *
+ *    // Since a is local, you also need the following additional permission,
+ *    // which will be checked by the MBeanServer 'srv2' that contains 'b':
+ *    //
+ *    // granting permission to access attribute 'Foo' of MBean b//D:k=v from
+ *    // 'srv2'
+ *    srv2::Foo[b//D:k=v]
+ * </pre>
+ * <p>On the other hand, if namespace <code>a</code> is a JMXRemoteNamespace
+ *    pointing to an MBeanServer in a remote JVM, then the only permission you
+ *    need to get the attribute <code>Foo</code> will be:
+ * </p>
+ * <pre>
+ *    // granting permission to access attribute 'Foo' of MBean a//b//D:k=v
+ *    // from 'srv1'
+ *    srv1::Foo[a//b//D:k=v]
+ * </pre>
+ * <p>The namespace <code>b</code> resides in the remote JVM, and
+ *    therefore the permissions concerning access to MBeans from
+ *    namespace 'b' will only be checked in the remote JVM, if that JVM is
+ *    configured to do so.
+ * </p>
+ *
+ * <p>The {@code <mbean name>} is written using the usual syntax for {@link
+ * ObjectName}.  It may contain any legal characters, including
+ * <code>]</code>.  It is terminated by a <code>]</code> character
+ * that is the last character in the string.
+ * </p>
+ * <p>Below are some examples of permission names:</p>
+ * <pre>
+ *    // allows access to Foo in 'a//b//*:*' from any MBeanServer in the JVM.
+ *    Foo[a//b//*:*]
+ *
+ *    // allows access to Foo in all subnamespaces of 'a//b', but only for
+ *    // MBeanServers whose name matches 'myapp.*'
+ *    myapp.*::Foo[a//b//*&#42;//*:*]
+ *
+ *    // allows access to Foo from all namespaces in the MBeanServer named
+ *    // 'myapp.srv1' - but not recursively.
+ *    myapp.srv1::Foo[&#42;//*:*]
+ * </pre>
+ * <p>For instance, the first two permissions listed above
+ *    will let through {@code getAttribute("a//b//D:k=v","Foo");} in
+ *    all MBeanServers, but will block access to
+ *    {@code getAttribute("a//b//c//D:k=v","Foo");} in MBeanServers whose
+ *    name do not start with {@code "myapp."}.
+ * </p>
+ * <p><a name="metawildcard">Depending on how your namespace hierarchy
+ *    is defined some of these wildcard permission names can be useful</a>:</p>
+ * <pre>
+ *    // allows access to Foo in all namespaces, recursively.
+ *    //
+ *    *::Foo[*&#42;//*:*]
+ *
+ *    // This permission name is the equivalent to the permission names above:
+ *    // Foo[*&#42;//*:*] and Foo[] are equivalent.
+ *    //
+ *    Foo[]
+ *
+ *    // This permission name is the equivalent to the two permission names
+ *    // above:
+ *    // Foo[*&#42;//*:*], Foo[], Foo are equivalent.
+ *    //
+ *    Foo
+ *
+ *    // allows access to Foo from all namespaces - but not recursively.
+ *    // This wildcard permission complements the previous one: it allows
+ *    // access to 'Foo' from an MBean directly registered in any local namespace.
+ *    //
+ *    Foo[&#42;//*:*]
+ *
+ * </pre>
+ * <p><b>Note on wildcards:</b> In an object name pattern, a path element
+ *    of exactly <code>**</code> corresponds to a meta
+ *    wildcard that will match any number of sub namespaces. Hence:</p>
+ * <ul>
+ * <table border="1">
+ * <thead><th>pattern</th><th>matches</th><th>doesn't match</th></thead>
+ * <tbody>
+ * <tr><td><code>*&#42;//D:k=v</code></td>
+ *     <td><code>a//D:k=v</code><br>
+ *         <code>a//b//D:k=v</code><br>
+ *         <code>a//b//c//D:k=v</code></td>
+ *     <td><code>D:k=v</code></td></tr>
+ * <tr><td><code>a//*&#42;//D:k=v</code></td>
+ *     <td><code>a//b//D:k=v</code><br>
+ *         <code>a//b//c//D:k=v</code></td>
+ *     <td><code>b//b//c//D:k=v</code><br>
+ *         <code>a//D:k=v</code><br>
+ *         <code>D:k=v</code></td></tr>
+ * <tr><td><code>a//*&#42;//e//D:k=v</code></td>
+ *     <td><code>a//b//e//D:k=v</code><br>
+ *         <code>a//b//c//e//D:k=v</code></td>
+ *     <td><code>a//b//c//c//D:k=v</code><br>
+ *         <code>b//b//c//e//D:k=v</code><br>
+ *         <code>a//e//D:k=v</code><br>
+ *         <code>e//D:k=v</code></td></tr>
+ * <tr><td><code>a//b*&#42;//e//D:k=v</code></td>
+ *      <td><code>a//b//e//D:k=v</code></td>
+ *      <td><code>a//b//c//e//D:k=v</code><br>
+ *          because in that case <code>b*&#42;</code><br>
+ *         is not a meta-wildcard - and <code>b**</code><br>
+ *         is thus equivalent to <code>b*</code>.</td></tr>
+ * </tbody>
+ * </table>
+ *</ul>
+ *
+ * <p>If {@code <mbean server name>::} is omitted, then one of
+ * <code>member</code> or <code>object name</code> may be omitted.
+ * If the <code>object name</code> is omitted,
+ * the <code>[]</code> may be too (but does not have to be).  It is
+ * not legal to omit all items, that is to have a <em>name</em>
+ * which is the empty string.</p>
+ * <p>If {@code <mbean server name>} is present, it <b>must be followed</b> by
+ *    the {@code "::"} separator - otherwise it will be interpreted as
+ *    a {@code member name}.
+ * </p>
+ *
+ * <p>
+ * One or more of the <a href="#MBeanServerName">MBean Server name</a>,
+ * <a href="#Member">member</a>
+ * or <a href="#MBeanName">object name</a> may be the character "<code>-</code>",
+ * which is equivalent to a null value.  A null value is implied by
+ * any value (including another null value) but does not imply any
+ * other value.
+ * </p>
+ *
+ * <p><a name="action-list">The possible actions are these:</a></p>
+ *
+ * <ul>
+ * <li>addNotificationListener</li>
+ * <li>getAttribute</li>
+ * <li>getClassLoader</li>
+ * <li>getClassLoaderFor</li>
+ * <li>getClassLoaderRepository</li>
+ * <li>getMBeanInfo</li>
+ * <li>getObjectInstance</li>
+ * <li>instantiate</li>
+ * <li>invoke</li>
+ * <li>isInstanceOf</li>
+ * <li>queryMBeans</li>
+ * <li>queryNames</li>
+ * <li>registerMBean</li>
+ * <li>removeNotificationListener</li>
+ * <li>setAttribute</li>
+ * <li>unregisterMBean</li>
+ * </ul>
+ *
+ * <p>In a comma-separated list of actions, spaces are allowed before
+ * and after each action.</p>
+ *
+ * @since 1.7
+ */
+public class JMXNamespacePermission extends Permission {
+
+    private static final long serialVersionUID = -2416928705275160661L;
+
+    private static final String WILDPATH = "**" +
+                JMXNamespaces.NAMESPACE_SEPARATOR + "*";
+
+    /**
+     * Actions list.
+     */
+    private static final int AddNotificationListener    = 0x00001;
+    private static final int GetAttribute               = 0x00002;
+    private static final int GetClassLoader             = 0x00004;
+    private static final int GetClassLoaderFor          = 0x00008;
+    private static final int GetClassLoaderRepository   = 0x00010;
+    // No GetDomains because it is not possible to route a call to
+    // getDomains() on a NamespaceInterceptor - getDomains() doesn't
+    // have any ObjectName.
+    // private static final int GetDomains                 = 0x00020;
+    private static final int GetMBeanInfo               = 0x00040;
+    private static final int GetObjectInstance          = 0x00080;
+    private static final int Instantiate                = 0x00100;
+    private static final int Invoke                     = 0x00200;
+    private static final int IsInstanceOf               = 0x00400;
+    private static final int QueryMBeans                = 0x00800;
+    private static final int QueryNames                 = 0x01000;
+    private static final int RegisterMBean              = 0x02000;
+    private static final int RemoveNotificationListener = 0x04000;
+    private static final int SetAttribute               = 0x08000;
+    private static final int UnregisterMBean            = 0x10000;
+
+    /**
+     * No actions.
+     */
+    private static final int NONE = 0x00000;
+
+    /**
+     * All actions.
+     */
+    // No GetDomains because it is not possible to route a call to
+    // getDomains() on a NamespaceInterceptor - getDomains() doesn't
+    // have any ObjectName.
+    //
+    private static final int ALL =
+        AddNotificationListener    |
+        GetAttribute               |
+        GetClassLoader             |
+        GetClassLoaderFor          |
+        GetClassLoaderRepository   |
+        GetMBeanInfo               |
+        GetObjectInstance          |
+        Instantiate                |
+        Invoke                     |
+        IsInstanceOf               |
+        QueryMBeans                |
+        QueryNames                 |
+        RegisterMBean              |
+        RemoveNotificationListener |
+        SetAttribute               |
+        UnregisterMBean;
+
+    /**
+     * The actions string.
+     */
+    private String actions;
+
+    /**
+     * The actions mask.
+     */
+    private transient int mask;
+
+    /**
+     * The name of the MBeanServer in which this permission is checked, or
+     * granted.  If null, is implied by any MBean server name
+     * but does not imply any non-null MBean server name.
+     */
+    private transient String mbeanServerName;
+
+    /**
+     * The member that must match.  If null, is implied by any member
+     * but does not imply any non-null member.
+     */
+    private transient String member;
+
+    /**
+     * The objectName that must match.  If null, is implied by any
+     * objectName but does not imply any non-null objectName.
+     */
+    private transient ObjectName objectName;
+
+    /**
+     * If objectName is missing from name, then allnames will be
+     * set to true.
+     */
+    private transient boolean  allnames = false;
+
+    /**
+     * Parse <code>actions</code> parameter.
+     */
+    private void parseActions() {
+
+        int amask;
+
+        if (actions == null)
+            throw new IllegalArgumentException("JMXNamespaceAccessPermission: " +
+                                               "actions can't be null");
+        if (actions.equals(""))
+            throw new IllegalArgumentException("JMXNamespaceAccessPermission: " +
+                                               "actions can't be empty");
+
+        amask = getMask(actions);
+
+        if ((amask & ALL) != amask)
+            throw new IllegalArgumentException("Invalid actions mask");
+        if (amask == NONE)
+            throw new IllegalArgumentException("Invalid actions mask");
+        this.mask = amask;
+    }
+
+    /**
+     * Parse <code>name</code> parameter.
+     */
+    private void parseName() {
+        String name = getName();
+
+        if (name == null)
+            throw new IllegalArgumentException("JMXNamespaceAccessPermission name " +
+                                               "cannot be null");
+
+        if (name.equals(""))
+            throw new IllegalArgumentException("JMXNamespaceAccessPermission name " +
+                                               "cannot be empty");
+        final int sepIndex = name.indexOf("::");
+        if (sepIndex < 0) {
+            setMBeanServerName("*");
+        } else {
+            setMBeanServerName(name.substring(0,sepIndex));
+        }
+
+        /* The name looks like "mbeanServerName::member[objectname]".
+           We subtract elements from the right as we parse, so after
+           parsing the objectname we have "class#member" and after parsing the
+           member we have "class".  Each element is optional.  */
+
+        // Parse ObjectName
+
+        final int start = (sepIndex<0)?0:sepIndex+2;
+        int openingBracket = name.indexOf("[",start);
+        if (openingBracket == -1) {
+            // If "[on]" missing then ObjectName("*:*")
+            //
+            objectName = null;
+            allnames = true;
+            openingBracket=name.length();
+        } else {
+            if (!name.endsWith("]")) {
+                throw new IllegalArgumentException("JMXNamespaceAccessPermission: " +
+                                                   "The ObjectName in the " +
+                                                   "target name must be " +
+                                                   "included in square " +
+                                                   "brackets");
+            } else {
+                // Create ObjectName
+                //
+                String on = name.substring(openingBracket + 1,
+                                           name.length() - 1);
+                try {
+                    // If "[]" then allnames are implied
+                    //
+                    final ObjectName target;
+                    final boolean    all;
+                    if (on.equals("")) {
+                        target = null;
+                        all = true;
+                    } else if (on.equals("-")) {
+                        target = null;
+                        all = false;
+                    } else {
+                        target = new ObjectName(on);
+                        all    = false;
+                    }
+                    setObjectName(target,all);
+                } catch (MalformedObjectNameException e) {
+                    throw new IllegalArgumentException(
+                            "JMXNamespaceAccessPermission: " +
+                            "The target name does " +
+                            "not specify a valid " +
+                            "ObjectName", e);
+                }
+            }
+        }
+
+        final String memberName = name.substring(start,openingBracket);
+        setMember(memberName);
+    }
+
+    private void setObjectName(ObjectName target, boolean all) {
+        if (target != null &&
+            !Util.wildpathmatch(target.getDomain(), WILDPATH)) {
+            throw new IllegalArgumentException(
+                    "The target name does not contain " +
+                    "any namespace: "+String.valueOf(target));
+        } else if (target != null) {
+            final String domain = target.getDomain();
+            final int seplen = JMXNamespaces.NAMESPACE_SEPARATOR.length();
+            final int sepc = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
+            if (sepc < 0 || (sepc+seplen)==domain.length()) {
+                throw new IllegalArgumentException(String.valueOf(target)+
+                        ": no namespace in domain");
+            }
+        }
+        objectName = target;
+        allnames = all;
+    }
+
+    /**
+     * Assign fields based on className, member, and objectName
+     * parameters.
+     */
+//    private void initName(String namespaceName, String member,
+//                          ObjectName objectName, boolean allnames) {
+//        setNamespace(namespaceName);
+    private void initName(String mbeanServerName, String member,
+                          ObjectName mbeanName, boolean all) {
+        setMBeanServerName(mbeanServerName);
+        setMember(member);
+        setObjectName(mbeanName, all);
+    }
+
+    private void setMBeanServerName(String mbeanServerName) {
+        if (mbeanServerName == null || mbeanServerName.equals("-")) {
+            this.mbeanServerName = null;
+        } else if (mbeanServerName.equals("")) {
+            this.mbeanServerName = "*";
+        } else {
+            this.mbeanServerName = mbeanServerName;
+        }
+    }
+
+    private void setMember(String member) {
+        if (member == null || member.equals("-"))
+            this.member = null;
+        else if (member.equals(""))
+            this.member = "*";
+        else
+            this.member = member;
+    }
+
+    /**
+     * <p>Create a new JMXNamespacePermission object with the
+     * specified target name and actions.</p>
+     *
+     * <p>The target name is of the form
+     * "<code>mbeanServerName::member[objectName]</code>" where each part is
+     * optional. This target name must not be empty or null.
+     * If <code>objectName</code> is present, it is of
+     * the form <code>namespace//MBeanName</code>.
+     * </p>
+     * <p>
+     * For a permission you need, {@code mbeanServerName} is the
+     * <a href="#MBeanServerName">name of the MBeanServer</a> from
+     * which {@code objectName} is being accessed.
+     * </p>
+     * <p>
+     * For a permission you have, {@code mbeanServerName} is the
+     * <a href="#MBeanServerName">name of the MBeanServer</a> from
+     * which access to {@code objectName} is granted.
+     * It can also be a pattern, and if omitted, {@code "*"} is assumed,
+     * meaning that access to {@code objectName} is granted in all
+     * MBean servers in the JVM.
+     * </p>
+     *
+     * <p>The actions parameter contains a comma-separated list of the
+     * desired actions granted on the target name.  It must not be
+     * empty or null.</p>
+     *
+     * @param name the triplet "mbeanServerName::member[objectName]".
+     * If <code>objectName</code> is present, it is of
+     * the form <code>namespace//MBeanName</code>.
+     * @param actions the action string.
+     *
+     * @exception IllegalArgumentException if the <code>name</code> or
+     * <code>actions</code> is invalid.
+     */
+    public JMXNamespacePermission(String name, String actions) {
+        super(name);
+
+        parseName();
+
+        this.actions = actions;
+        parseActions();
+    }
+
+    /**
+     * <p>Create a new JMXNamespacePermission object with the specified
+     * target name (namespace name, member, object name) and actions.</p>
+     *
+     * <p>The {@code MBeanServer} name, member and object name
+     * parameters define a target name of the form
+     * "<code>mbeanServerName::member[objectName]</code>" where each
+     * part is optional.  This will be the result of {@link #getName()} on the
+     * resultant JMXNamespacePermission.
+     * If the <code>mbeanServerName</code> is empty or exactly {@code "*"}, then
+     * "{@code mbeanServerName::}" is omitted in that result.
+     * </p>
+     *
+     * <p>The actions parameter contains a comma-separated list of the
+     * desired actions granted on the target name.  It must not be
+     * empty or null.</p>
+     *
+     * @param mbeanServerName the name of the {@code MBeanServer} to which this
+     * permission applies.
+     * May be null or <code>"-"</code>, which represents an MBeanServer name
+     * that is implied by any MBeanServer name but does not imply any other
+     * MBeanServer name.
+     * @param member the member to which this permission applies.  May
+     * be null or <code>"-"</code>, which represents a member that is
+     * implied by any member but does not imply any other member.
+     * @param objectName the object name to which this permission
+     * applies.
+     * May be null, which represents an object name that is
+     * implied by any object name but does not imply any other object
+     * name. If not null, the {@code objectName} must be of the
+     * form {@code <namespace>//<mbean name>} - where {@code <namespace>}
+     * can be a domain pattern, and {@code <mbean name>} can be an ObjectName
+     * pattern.
+     * For a permission you need, {@code <namespace>} is the name of the
+     * name space for which the permission is checked, and {@code <mbean name>}
+     * is the name of the MBean in that namespace.
+     * The composed name {@code <namespace>//<mbean name>} thus represents the
+     * name of the MBean as seen by the {@code mbeanServerName} containing
+     * {@code <namespace>}.
+     *
+     * @param actions the action string.
+     */
+    public JMXNamespacePermission(
+                           String mbeanServerName,
+                           String member,
+                           ObjectName objectName,
+                           String actions) {
+        this(mbeanServerName, member, objectName, false, actions);
+//        this(member, objectName, false, actions);
+    }
+
+    /**
+     * <p>Create a new JMXNamespacePermission object with the specified
+     * MBean Server name, member, and actions.</p>
+     *
+     * <p>The {@code MBeanServer} name and member
+     * parameters define a target name of the form
+     * "<code>mbeanServerName::member[]</code>" where each
+     * part is optional.  This will be the result of {@link #getName()} on the
+     * resultant JMXNamespacePermission.
+     * If the <code>mbeanServerName</code> is empty or exactly {@code "*"}, then
+     * "{@code mbeanServerName::}" is omitted in that result.
+     * </p>
+     *
+     * <p>The actions parameter contains a comma-separated list of the
+     * desired actions granted on the target name.  It must not be
+     * empty or null.</p>
+     *
+     * @param mbeanServerName the name of the {@code MBeanServer} to which this
+     * permission applies.
+     * May be null or <code>"-"</code>, which represents an MBeanServer name
+     * that is implied by any MBeanServer name but does not imply any other
+     * MBeanServer name.
+     * @param member the member to which this permission applies.  May
+     * be null or <code>"-"</code>, which represents a member that is
+     * implied by any member but does not imply any other member.
+     * @param actions the action string.
+     */
+    public JMXNamespacePermission(String mbeanServerName,
+                           String member,
+                           String actions) {
+        this(mbeanServerName,member,null,true,actions);
+        // this(member,null,allnames,actions);
+    }
+
+    /**
+     * <p>Create a new JMXNamespacePermission object with the specified
+     * target name (namespace name, member, object name) and actions.</p>
+     *
+     * <p>The MBean Server name, member and object name parameters define a
+     * target name of the form
+     * "<code>mbeanServerName::member[objectName]</code>" where each part is
+     * optional.  This will be the result of {@link
+     * java.security.Permission#getName() getName()} on the
+     * resultant JMXNamespacePermission.</p>
+     *
+     * <p>The actions parameter contains a comma-separated list of the
+     * desired actions granted on the target name.  It must not be
+     * empty or null.</p>
+     *
+     * @param mbeanServerName the name of the {@code MBeanServer} to which this
+     * permission applies.
+     * May be null or <code>"-"</code>, which represents an MBeanServer name
+     * that is implied by any MBeanServer name but does not imply any other
+     * MBeanServer name.
+     * @param member the member to which this permission applies.  May
+     * be null or <code>"-"</code>, which represents a member that is
+     * implied by any member but does not imply any other member.
+     * @param objectName the object name to which this permission
+     * applies.  If null, and allnames is false, represents an object
+     * name that is implied by any object name but does not imply any
+     * other object name. Otherwise, if allnames is true, it represents
+     * a meta wildcard that matches all object names. It is equivalent to
+     * a missing objectName ("[]") in the {@link
+     * java.security.Permission#getName() name} property.
+     * @param allnames represent a meta wildcard indicating that the
+     *        objectName was not specified. This implies all objectnames
+     *        that match "*:*" and all object names that match
+     *        "*&#42;//*:*"
+     * @param actions the action string.
+     */
+    private JMXNamespacePermission(String mbeanServerName,
+                           String member,
+                           ObjectName objectName,
+                           boolean allnames,
+                           String actions) {
+
+        super(makeName(mbeanServerName,
+                member, objectName, allnames));
+        initName(mbeanServerName,
+                member, objectName, allnames);
+
+        this.actions = actions;
+        parseActions();
+    }
+
+    private static String makeName(String mbeanServerName,
+            String memberName, ObjectName objName, boolean allMBeans) {
+        final StringBuilder name = new StringBuilder();
+        if (mbeanServerName == null)
+            mbeanServerName = "-";
+        if (!mbeanServerName.equals("") && !mbeanServerName.equals("*"))
+            name.append(mbeanServerName).append("::");
+        if (memberName == null)
+            memberName = "-";
+        name.append(memberName);
+        if (objName == null) {
+            if (allMBeans)
+                name.append("[]");
+            else
+                name.append("[-]");
+        } else {
+            final String domain = objName.getDomain();
+            final int seplen = JMXNamespaces.NAMESPACE_SEPARATOR.length();
+            final int sepc = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
+            if (sepc < 0 || (sepc+seplen)==domain.length()) {
+                throw new IllegalArgumentException(String.valueOf(objName)+
+                        ": no namespace in domain");
+            }
+            final String can = objName.getCanonicalName();
+            name.append("[").append(can).append("]");
+        }
+        return name.toString();
+    }
+
+    /**
+     * Returns the "canonical string representation" of the actions. That is,
+     * this method always returns actions in alphabetical order.
+     *
+     * @return the canonical string representation of the actions.
+     */
+    public String getActions() {
+
+        if (actions == null)
+            actions = getActions(this.mask);
+
+        return actions;
+    }
+
+    /**
+     * Returns the "canonical string representation"
+     * of the actions from the mask.
+     */
+    private static String getActions(int mask) {
+        final StringBuilder sb = new StringBuilder();
+        boolean comma = false;
+
+        if ((mask & AddNotificationListener) == AddNotificationListener) {
+            comma = true;
+            sb.append("addNotificationListener");
+        }
+
+        if ((mask & GetAttribute) == GetAttribute) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getAttribute");
+        }
+
+        if ((mask & GetClassLoader) == GetClassLoader) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getClassLoader");
+        }
+
+        if ((mask & GetClassLoaderFor) == GetClassLoaderFor) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getClassLoaderFor");
+        }
+
+        if ((mask & GetClassLoaderRepository) == GetClassLoaderRepository) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getClassLoaderRepository");
+        }
+
+        if ((mask & GetMBeanInfo) == GetMBeanInfo) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getMBeanInfo");
+        }
+
+        if ((mask & GetObjectInstance) == GetObjectInstance) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("getObjectInstance");
+        }
+
+        if ((mask & Instantiate) == Instantiate) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("instantiate");
+        }
+
+        if ((mask & Invoke) == Invoke) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("invoke");
+        }
+
+        if ((mask & IsInstanceOf) == IsInstanceOf) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("isInstanceOf");
+        }
+
+        if ((mask & QueryMBeans) == QueryMBeans) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("queryMBeans");
+        }
+
+        if ((mask & QueryNames) == QueryNames) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("queryNames");
+        }
+
+        if ((mask & RegisterMBean) == RegisterMBean) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("registerMBean");
+        }
+
+        if ((mask & RemoveNotificationListener) == RemoveNotificationListener) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("removeNotificationListener");
+        }
+
+        if ((mask & SetAttribute) == SetAttribute) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("setAttribute");
+        }
+
+        if ((mask & UnregisterMBean) == UnregisterMBean) {
+            if (comma) sb.append(',');
+            else comma = true;
+            sb.append("unregisterMBean");
+        }
+
+        // No GetDomains because it is not possible to route a call to
+        // getDomains() on a NamespaceInterceptor - getDomains() doesn't
+        // have any ObjectName.
+
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return this.getName().hashCode() + this.getActions().hashCode();
+    }
+
+    /**
+     * Converts an action String to an integer action mask.
+     *
+     * @param action the action string.
+     * @return the action mask.
+     */
+    private static int getMask(String action) {
+
+        /*
+         * BE CAREFUL HERE! PARSING ORDER IS IMPORTANT IN THIS ALGORITHM.
+         *
+         * The 'string length' test must be performed for the lengthiest
+         * strings first.
+         *
+         * In this permission if the "unregisterMBean" string length test is
+         * performed after the "registerMBean" string length test the algorithm
+         * considers the 'unregisterMBean' action as being the 'registerMBean'
+         * action and a parsing error is returned.
+         */
+
+        int mask = NONE;
+
+        if (action == null) {
+            return mask;
+        }
+
+        if (action.equals("*")) {
+            return ALL;
+        }
+
+        char[] a = action.toCharArray();
+
+        int i = a.length - 1;
+        if (i < 0)
+            return mask;
+
+        while (i != -1) {
+            char c;
+
+            // skip whitespace
+            while ((i!=-1) && ((c = a[i]) == ' ' ||
+                               c == '\r' ||
+                               c == '\n' ||
+                               c == '\f' ||
+                               c == '\t'))
+                i--;
+
+            // check for the known strings
+            int matchlen;
+
+            // No GetDomains because it is not possible to route a call to
+            // getDomains() on a NamespaceInterceptor - getDomains() doesn't
+            // have any ObjectName.
+
+            if (i >= 25 && /* removeNotificationListener */
+                (a[i-25] == 'r') &&
+                (a[i-24] == 'e') &&
+                (a[i-23] == 'm') &&
+                (a[i-22] == 'o') &&
+                (a[i-21] == 'v') &&
+                (a[i-20] == 'e') &&
+                (a[i-19] == 'N') &&
+                (a[i-18] == 'o') &&
+                (a[i-17] == 't') &&
+                (a[i-16] == 'i') &&
+                (a[i-15] == 'f') &&
+                (a[i-14] == 'i') &&
+                (a[i-13] == 'c') &&
+                (a[i-12] == 'a') &&
+                (a[i-11] == 't') &&
+                (a[i-10] == 'i') &&
+                (a[i-9] == 'o') &&
+                (a[i-8] == 'n') &&
+                (a[i-7] == 'L') &&
+                (a[i-6] == 'i') &&
+                (a[i-5] == 's') &&
+                (a[i-4] == 't') &&
+                (a[i-3] == 'e') &&
+                (a[i-2] == 'n') &&
+                (a[i-1] == 'e') &&
+                (a[i] == 'r')) {
+                matchlen = 26;
+                mask |= RemoveNotificationListener;
+            } else if (i >= 23 && /* getClassLoaderRepository */
+                       (a[i-23] == 'g') &&
+                       (a[i-22] == 'e') &&
+                       (a[i-21] == 't') &&
+                       (a[i-20] == 'C') &&
+                       (a[i-19] == 'l') &&
+                       (a[i-18] == 'a') &&
+                       (a[i-17] == 's') &&
+                       (a[i-16] == 's') &&
+                       (a[i-15] == 'L') &&
+                       (a[i-14] == 'o') &&
+                       (a[i-13] == 'a') &&
+                       (a[i-12] == 'd') &&
+                       (a[i-11] == 'e') &&
+                       (a[i-10] == 'r') &&
+                       (a[i-9] == 'R') &&
+                       (a[i-8] == 'e') &&
+                       (a[i-7] == 'p') &&
+                       (a[i-6] == 'o') &&
+                       (a[i-5] == 's') &&
+                       (a[i-4] == 'i') &&
+                       (a[i-3] == 't') &&
+                       (a[i-2] == 'o') &&
+                       (a[i-1] == 'r') &&
+                       (a[i] == 'y')) {
+                matchlen = 24;
+                mask |= GetClassLoaderRepository;
+            } else if (i >= 22 && /* addNotificationListener */
+                       (a[i-22] == 'a') &&
+                       (a[i-21] == 'd') &&
+                       (a[i-20] == 'd') &&
+                       (a[i-19] == 'N') &&
+                       (a[i-18] == 'o') &&
+                       (a[i-17] == 't') &&
+                       (a[i-16] == 'i') &&
+                       (a[i-15] == 'f') &&
+                       (a[i-14] == 'i') &&
+                       (a[i-13] == 'c') &&
+                       (a[i-12] == 'a') &&
+                       (a[i-11] == 't') &&
+                       (a[i-10] == 'i') &&
+                       (a[i-9] == 'o') &&
+                       (a[i-8] == 'n') &&
+                       (a[i-7] == 'L') &&
+                       (a[i-6] == 'i') &&
+                       (a[i-5] == 's') &&
+                       (a[i-4] == 't') &&
+                       (a[i-3] == 'e') &&
+                       (a[i-2] == 'n') &&
+                       (a[i-1] == 'e') &&
+                       (a[i] == 'r')) {
+                matchlen = 23;
+                mask |= AddNotificationListener;
+            } else if (i >= 16 && /* getClassLoaderFor */
+                       (a[i-16] == 'g') &&
+                       (a[i-15] == 'e') &&
+                       (a[i-14] == 't') &&
+                       (a[i-13] == 'C') &&
+                       (a[i-12] == 'l') &&
+                       (a[i-11] == 'a') &&
+                       (a[i-10] == 's') &&
+                       (a[i-9] == 's') &&
+                       (a[i-8] == 'L') &&
+                       (a[i-7] == 'o') &&
+                       (a[i-6] == 'a') &&
+                       (a[i-5] == 'd') &&
+                       (a[i-4] == 'e') &&
+                       (a[i-3] == 'r') &&
+                       (a[i-2] == 'F') &&
+                       (a[i-1] == 'o') &&
+                       (a[i] == 'r')) {
+                matchlen = 17;
+                mask |= GetClassLoaderFor;
+            } else if (i >= 16 && /* getObjectInstance */
+                       (a[i-16] == 'g') &&
+                       (a[i-15] == 'e') &&
+                       (a[i-14] == 't') &&
+                       (a[i-13] == 'O') &&
+                       (a[i-12] == 'b') &&
+                       (a[i-11] == 'j') &&
+                       (a[i-10] == 'e') &&
+                       (a[i-9] == 'c') &&
+                       (a[i-8] == 't') &&
+                       (a[i-7] == 'I') &&
+                       (a[i-6] == 'n') &&
+                       (a[i-5] == 's') &&
+                       (a[i-4] == 't') &&
+                       (a[i-3] == 'a') &&
+                       (a[i-2] == 'n') &&
+                       (a[i-1] == 'c') &&
+                       (a[i] == 'e')) {
+                matchlen = 17;
+                mask |= GetObjectInstance;
+            } else if (i >= 14 && /* unregisterMBean */
+                       (a[i-14] == 'u') &&
+                       (a[i-13] == 'n') &&
+                       (a[i-12] == 'r') &&
+                       (a[i-11] == 'e') &&
+                       (a[i-10] == 'g') &&
+                       (a[i-9] == 'i') &&
+                       (a[i-8] == 's') &&
+                       (a[i-7] == 't') &&
+                       (a[i-6] == 'e') &&
+                       (a[i-5] == 'r') &&
+                       (a[i-4] == 'M') &&
+                       (a[i-3] == 'B') &&
+                       (a[i-2] == 'e') &&
+                       (a[i-1] == 'a') &&
+                       (a[i] == 'n')) {
+                matchlen = 15;
+                mask |= UnregisterMBean;
+            } else if (i >= 13 && /* getClassLoader */
+                       (a[i-13] == 'g') &&
+                       (a[i-12] == 'e') &&
+                       (a[i-11] == 't') &&
+                       (a[i-10] == 'C') &&
+                       (a[i-9] == 'l') &&
+                       (a[i-8] == 'a') &&
+                       (a[i-7] == 's') &&
+                       (a[i-6] == 's') &&
+                       (a[i-5] == 'L') &&
+                       (a[i-4] == 'o') &&
+                       (a[i-3] == 'a') &&
+                       (a[i-2] == 'd') &&
+                       (a[i-1] == 'e') &&
+                       (a[i] == 'r')) {
+                matchlen = 14;
+                mask |= GetClassLoader;
+            } else if (i >= 12 && /* registerMBean */
+                       (a[i-12] == 'r') &&
+                       (a[i-11] == 'e') &&
+                       (a[i-10] == 'g') &&
+                       (a[i-9] == 'i') &&
+                       (a[i-8] == 's') &&
+                       (a[i-7] == 't') &&
+                       (a[i-6] == 'e') &&
+                       (a[i-5] == 'r') &&
+                       (a[i-4] == 'M') &&
+                       (a[i-3] == 'B') &&
+                       (a[i-2] == 'e') &&
+                       (a[i-1] == 'a') &&
+                       (a[i] == 'n')) {
+                matchlen = 13;
+                mask |= RegisterMBean;
+            } else if (i >= 11 && /* getAttribute */
+                       (a[i-11] == 'g') &&
+                       (a[i-10] == 'e') &&
+                       (a[i-9] == 't') &&
+                       (a[i-8] == 'A') &&
+                       (a[i-7] == 't') &&
+                       (a[i-6] == 't') &&
+                       (a[i-5] == 'r') &&
+                       (a[i-4] == 'i') &&
+                       (a[i-3] == 'b') &&
+                       (a[i-2] == 'u') &&
+                       (a[i-1] == 't') &&
+                       (a[i] == 'e')) {
+                matchlen = 12;
+                mask |= GetAttribute;
+            } else if (i >= 11 && /* getMBeanInfo */
+                       (a[i-11] == 'g') &&
+                       (a[i-10] == 'e') &&
+                       (a[i-9] == 't') &&
+                       (a[i-8] == 'M') &&
+                       (a[i-7] == 'B') &&
+                       (a[i-6] == 'e') &&
+                       (a[i-5] == 'a') &&
+                       (a[i-4] == 'n') &&
+                       (a[i-3] == 'I') &&
+                       (a[i-2] == 'n') &&
+                       (a[i-1] == 'f') &&
+                       (a[i] == 'o')) {
+                matchlen = 12;
+                mask |= GetMBeanInfo;
+            } else if (i >= 11 && /* isInstanceOf */
+                       (a[i-11] == 'i') &&
+                       (a[i-10] == 's') &&
+                       (a[i-9] == 'I') &&
+                       (a[i-8] == 'n') &&
+                       (a[i-7] == 's') &&
+                       (a[i-6] == 't') &&
+                       (a[i-5] == 'a') &&
+                       (a[i-4] == 'n') &&
+                       (a[i-3] == 'c') &&
+                       (a[i-2] == 'e') &&
+                       (a[i-1] == 'O') &&
+                       (a[i] == 'f')) {
+                matchlen = 12;
+                mask |= IsInstanceOf;
+            } else if (i >= 11 && /* setAttribute */
+                       (a[i-11] == 's') &&
+                       (a[i-10] == 'e') &&
+                       (a[i-9] == 't') &&
+                       (a[i-8] == 'A') &&
+                       (a[i-7] == 't') &&
+                       (a[i-6] == 't') &&
+                       (a[i-5] == 'r') &&
+                       (a[i-4] == 'i') &&
+                       (a[i-3] == 'b') &&
+                       (a[i-2] == 'u') &&
+                       (a[i-1] == 't') &&
+                       (a[i] == 'e')) {
+                matchlen = 12;
+                mask |= SetAttribute;
+            } else if (i >= 10 && /* instantiate */
+                       (a[i-10] == 'i') &&
+                       (a[i-9] == 'n') &&
+                       (a[i-8] == 's') &&
+                       (a[i-7] == 't') &&
+                       (a[i-6] == 'a') &&
+                       (a[i-5] == 'n') &&
+                       (a[i-4] == 't') &&
+                       (a[i-3] == 'i') &&
+                       (a[i-2] == 'a') &&
+                       (a[i-1] == 't') &&
+                       (a[i] == 'e')) {
+                matchlen = 11;
+                mask |= Instantiate;
+            } else if (i >= 10 && /* queryMBeans */
+                       (a[i-10] == 'q') &&
+                       (a[i-9] == 'u') &&
+                       (a[i-8] == 'e') &&
+                       (a[i-7] == 'r') &&
+                       (a[i-6] == 'y') &&
+                       (a[i-5] == 'M') &&
+                       (a[i-4] == 'B') &&
+                       (a[i-3] == 'e') &&
+                       (a[i-2] == 'a') &&
+                       (a[i-1] == 'n') &&
+                       (a[i] == 's')) {
+                matchlen = 11;
+                mask |= QueryMBeans;
+            } else if (i >= 9 && /* queryNames */
+                       (a[i-9] == 'q') &&
+                       (a[i-8] == 'u') &&
+                       (a[i-7] == 'e') &&
+                       (a[i-6] == 'r') &&
+                       (a[i-5] == 'y') &&
+                       (a[i-4] == 'N') &&
+                       (a[i-3] == 'a') &&
+                       (a[i-2] == 'm') &&
+                       (a[i-1] == 'e') &&
+                       (a[i] == 's')) {
+                matchlen = 10;
+                mask |= QueryNames;
+            } else if (i >= 5 && /* invoke */
+                       (a[i-5] == 'i') &&
+                       (a[i-4] == 'n') &&
+                       (a[i-3] == 'v') &&
+                       (a[i-2] == 'o') &&
+                       (a[i-1] == 'k') &&
+                       (a[i] == 'e')) {
+                matchlen = 6;
+                mask |= Invoke;
+            } else {
+                // parse error
+                throw new IllegalArgumentException("Invalid permission: " +
+                                                   action);
+            }
+
+            // make sure we didn't just match the tail of a word
+            // like "ackbarfaccept".  Also, skip to the comma.
+            boolean seencomma = false;
+            while (i >= matchlen && !seencomma) {
+                switch(a[i-matchlen]) {
+                case ',':
+                    seencomma = true;
+                    break;
+                case ' ': case '\r': case '\n':
+                case '\f': case '\t':
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid permission: " +
+                                                       action);
+                }
+                i--;
+            }
+
+            // point i at the location of the comma minus one (or -1).
+            i -= matchlen;
+        }
+
+        return mask;
+    }
+
+    /**
+     * <p>Checks if this JMXNamespacePermission object "implies" the
+     * specified permission.</p>
+     *
+     * <p>More specifically, this method returns true if:</p>
+     *
+     * <ul>
+     *
+     * <li> <i>p</i> is an instance of JMXNamespacePermission; and</li>
+     *
+     * <li> <i>p</i> has a null mbeanServerName or <i>p</i>'s mbeanServerName
+     * matches this object's mbeanServerName; and</li>
+     *
+     * <li> <i>p</i> has a null member or <i>p</i>'s member matches this
+     * object's member; and</li>
+     *
+     * <li> <i>p</i> has a null object name or <i>p</i>'s
+     * object name matches this object's object name; and</li>
+     *
+     * <li> <i>p</i>'s actions are a subset of this object's actions</li>
+     *
+     * </ul>
+     *
+     * <p>If this object's mbeanServerName is a pattern, then <i>p</i>'s
+     *    mbeanServerName is matched against that pattern. An empty
+     *    mbeanServerName is equivalent to "{@code *}". A null
+     *    mbeanServerName is equivalent to "{@code -}".</p>
+     * <p>If this object's mbeanServerName is "<code>*</code>" or is
+     * empty, <i>p</i>'s mbeanServerName always matches it.</p>
+     *
+     * <p>If this object's member is "<code>*</code>", <i>p</i>'s
+     * member always matches it.</p>
+     *
+     * <p>If this object's objectName <i>n1</i> is an object name pattern,
+     * <i>p</i>'s objectName <i>n2</i> matches it if
+     * {@link ObjectName#equals <i>n1</i>.equals(<i>n2</i>)} or if
+     * {@link ObjectName#apply <i>n1</i>.apply(<i>n2</i>)}.</p>
+     *
+     * <p>A permission that includes the <code>queryMBeans</code> action
+     * is considered to include <code>queryNames</code> as well.</p>
+     *
+     * @param p the permission to check against.
+     * @return true if the specified permission is implied by this object,
+     * false if not.
+     */
+    public boolean implies(Permission p) {
+        if (!(p instanceof JMXNamespacePermission))
+            return false;
+
+        JMXNamespacePermission that = (JMXNamespacePermission) p;
+
+        // Actions
+        //
+        // The actions in 'this' permission must be a
+        // superset of the actions in 'that' permission
+        //
+
+        /* "queryMBeans" implies "queryNames" */
+        if ((this.mask & QueryMBeans) == QueryMBeans) {
+            if (((this.mask | QueryNames) & that.mask) != that.mask) {
+                //System.out.println("action [with QueryNames] does not imply");
+                return false;
+            }
+        } else {
+            if ((this.mask & that.mask) != that.mask) {
+                //System.out.println("action does not imply");
+                return false;
+            }
+        }
+
+        // Target name
+        //
+        // The 'mbeanServerName' check is true iff:
+        // 1) the mbeanServerName in 'this' permission is omitted or "*", or
+        // 2) the mbeanServerName in 'that' permission is omitted or "*", or
+        // 3) the mbeanServerName in 'this' permission does pattern
+        //    matching with the mbeanServerName in 'that' permission.
+        //
+        // The 'member' check is true iff:
+        // 1) the member in 'this' member is omitted or "*", or
+        // 2) the member in 'that' member is omitted or "*", or
+        // 3) the member in 'this' permission equals the member in
+        //    'that' permission.
+        //
+        // The 'object name' check is true iff:
+        // 1) the object name in 'this' permission is omitted, or
+        // 2) the object name in 'that' permission is omitted, or
+        // 3) the object name in 'this' permission does pattern
+        //    matching with the object name in 'that' permission.
+        //
+
+        if (that.mbeanServerName == null) {
+            // bottom is implied
+        } else if (this.mbeanServerName == null) {
+            // bottom implies nothing but itself
+            return false;
+        } else if (that.mbeanServerName.equals(this.mbeanServerName)) {
+            // exact match
+        } else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) {
+            return false; // no match
+        }
+
+        /* Check if this.member implies that.member */
+
+        if (that.member == null) {
+            // bottom is implied
+        } else if (this.member == null) {
+            // bottom implies nothing but itself
+            return false;
+        } else if (this.member.equals("*")) {
+            // wildcard implies everything (including itself)
+        } else if (this.member.equals(that.member)) {
+            // exact match
+        } else if (!Util.wildmatch(that.member,this.member)) {
+            return false; // no match
+        }
+
+        /* Check if this.objectName implies that.objectName */
+
+        if (that.objectName == null) {
+            // bottom is implied
+        } else if (this.objectName == null) {
+            // bottom implies nothing but itself
+            if (allnames == false) return false;
+        } else if (!this.objectName.apply(that.objectName)) {
+            /* ObjectName.apply returns false if that.objectName is a
+               wildcard so we also allow equals for that case.  This
+               never happens during real permission checks, but means
+               the implies relation is reflexive.  */
+            if (!this.objectName.equals(that.objectName))
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks two JMXNamespacePermission objects for equality. Checks
+     * that <i>obj</i> is an JMXNamespacePermission, and has the same
+     * name and actions as this object.
+     * <P>
+     * @param obj the object we are testing for equality with this object.
+     * @return true if obj is an JMXNamespacePermission, and has the
+     * same name and actions as this JMXNamespacePermission object.
+     */
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+
+        if (! (obj instanceof JMXNamespacePermission))
+            return false;
+
+        JMXNamespacePermission that = (JMXNamespacePermission) obj;
+
+        return (this.mask == that.mask) &&
+            (this.getName().equals(that.getName()));
+    }
+
+    /**
+     * Deserialize this object based on its name and actions.
+     */
+    private void readObject(ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        parseName();
+        parseActions();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXNamespaceView.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,300 @@
+/*
+ * 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.Set;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+/**
+ * This class makes it possible to navigate easily within a hierarchical
+ * namespace view.
+ *
+ * <pre>
+ * MBeanServerConnnection rootConnection = ...;
+ *
+ * // create a view at the local root of the namespace hierarchy.
+ * //
+ * JMXNamespaceView view = new JMXNamespaceView(rootConnection);
+ *
+ * // list all top level namespaces
+ * String[] list = view.list();
+ *
+ * // select one namespace from the list
+ * String whereToGo = ... ;
+ *
+ * // go down to the selected namespace:
+ * view = view.down(whereToGo);
+ * System.out.println("I am now in: " + view.where());
+ * System.out.println("I can see these MBeans:" +
+ *    view.getMBeanServerConnection().queryNames(null,null));
+ *
+ * // list sub namespaces in current view ('whereToGo')
+ * list = view.list();
+ * System.out.println("Here are the sub namespaces of "+view.where()+": "+
+ *                    Arrays.toString(list));
+ *
+ * // go up one level
+ * view = view.up();
+ * System.out.println("I am now back to: " +
+ *    (view.isRoot() ? "root namespace" : view.where()));
+ * </pre>
+ * @since 1.7
+ */
+public class JMXNamespaceView {
+
+    private static final ObjectName ALL_NAMESPACES;
+    static {
+        try {
+            ALL_NAMESPACES = ObjectName.getInstance("*" +
+                    JMXNamespaces.NAMESPACE_SEPARATOR + ":"+
+                    JMXNamespace.TYPE_ASSIGNMENT);
+        } catch (MalformedObjectNameException x) {
+            throw new ExceptionInInitializerError(x);
+        }
+    }
+    private static final int NAMESPACE_SEPARATOR_LENGTH =
+            JMXNamespaces.NAMESPACE_SEPARATOR.length();
+
+    private final JMXNamespaceView parent;
+    private final MBeanServerConnection here;
+    private final String where;
+
+    private static MBeanServerConnection checkRoot(MBeanServerConnection root) {
+        if (root == null)
+            throw new IllegalArgumentException(
+                    "namespaceRoot: null is not a valid value");
+        return root;
+    }
+
+    /**
+     * Creates a view at the top of a JMX namespace hierarchy.
+     * @param namespaceRoot The {@code MBeanServerConnection} at the
+     *        top of the hierarchy.
+     */
+    public JMXNamespaceView(MBeanServerConnection namespaceRoot) {
+        this(null,checkRoot(namespaceRoot),"");
+    }
+
+    // This constructor should remain private. A user can only create
+    // JMXNamespaceView at the top of the hierarchy.
+    // JMXNamespaceView sub nodes are created by their parent nodes.
+    private JMXNamespaceView(JMXNamespaceView parent,
+            MBeanServerConnection here, String where) {
+        this.parent = parent;
+        this.here   = here;
+        this.where  = where;
+    }
+
+    /**
+     * Returns the path leading to the namespace in this view, from
+     * the top of the hierarchy.
+     * @return The path to the namespace in this view.
+     */
+    public String where() {
+        return where;
+    }
+
+    /**
+     * Lists all direct sub namespaces in this view.  The returned strings
+     * do not contain the {@code //} separator.
+     *
+     * @return A list of direct sub name spaces accessible from this
+     *         namespace.
+     * @throws IOException if the attempt to list the namespaces fails because
+     * of a communication problem.
+     */
+    public String[] list() throws IOException {
+        final Set<ObjectName> names =
+                here.queryNames(ALL_NAMESPACES,null);
+        final String[] res = new String[names.size()];
+        int i = 0;
+        for (ObjectName dirName : names) {
+            final String dir = dirName.getDomain();
+            res[i++]=dir.substring(0,dir.length()-NAMESPACE_SEPARATOR_LENGTH);
+        }
+        return res;
+    }
+
+    /**
+     * Go down into a sub namespace.
+     * @param namespace the namespace to go down to.  It can contain one or
+     * more {@code //} separators, to traverse intermediate namespaces, but
+     * it must not begin or end with {@code //} or contain an empty
+     * intermediate namespace.  If it is the empty string, then {@code this} is
+     * returned.
+     * @return A view of the named sub namespace.
+     * @throws IllegalArgumentException if the {@code namespace} begins or
+     * ends with {@code //}.
+     */
+    public JMXNamespaceView down(String namespace) {
+        if (namespace.equals("")) return this;
+        if (namespace.startsWith(JMXNamespaces.NAMESPACE_SEPARATOR))
+            throw new IllegalArgumentException(namespace+": can't start with "+
+                    JMXNamespaces.NAMESPACE_SEPARATOR);
+
+        // This is a convenience to handle paths like xxx//yyy
+        final String[] elts =
+                namespace.split(JMXNamespaces.NAMESPACE_SEPARATOR);
+
+        // Go down the path, creating all sub namespaces along the way.
+        // Usually there will be a single element in the given namespace
+        // name, but we don't want to forbid things like
+        // down("xxx//yyy/www");
+        //
+        JMXNamespaceView previous = this;
+        String cursor = where;
+        for (String elt : elts) {
+            // empty path elements are not allowed. It means we
+            // had something like "xxx////yyy"
+            if (elt.equals(""))
+                throw new IllegalArgumentException(namespace+
+                        ": invalid path element");
+
+            // compute the "where" for the child.
+            cursor = JMXNamespaces.concat(cursor, elt);
+
+            // create the child...
+            final JMXNamespaceView next =
+                    makeJMXNamespaceView(root(), previous, cursor);
+
+            // the current child will be the parent of the next child...
+            previous = next;
+        }
+
+        // We return the last child that was created.
+        return previous;
+    }
+
+    /**
+     * Go back up one level. If this view is at the root of the
+     * hierarchy, returns {@code null}.
+     * @return A view of the parent namespace, or {@code null} if we're at
+     *         the root of the hierarchy.
+     */
+    public JMXNamespaceView up() {
+        return parent;
+    }
+
+    /**
+     * Tells whether this view is at the root of the hierarchy.
+     * @return {@code true} if this view is at the root of the hierachy.
+     */
+    public boolean isRoot() {
+        return parent == null;
+    }
+
+    /**
+     * Returns the view at the root of the hierarchy.
+     * If we are already at the root, this is {@code this}.
+     * @return the view at the root of the hierarchy.
+     */
+    public JMXNamespaceView root() {
+        if (parent == null) return this;
+        return parent.root();
+    }
+
+    /**
+     * A MBeanServerConnection to the namespace shown by this view.
+     * This is what would have been obtained by doing:
+     * <pre>
+     *   JMX.narrowToNamespace(this.root().getMBeanServerConnection(),
+     *       this.where());
+     * </pre>
+     * @return A MBeanServerConnection to the namespace shown by this view.
+     */
+    public MBeanServerConnection getMBeanServerConnection() {
+        return here;
+    }
+
+    /**
+     * <p>Get the name of the JMXNamespaceMBean handling the namespace shown by
+     * this view, relative to the root of the hierarchy.  If we are at the root
+     * of the hierarchy, this method returns {@code null}.</p>
+     *
+     * <p>You can use this method to make a proxy for the JMXNamespaceMBean
+     * as follows:</p>
+     *
+     * <pre>
+     * JMXNamespaceView view = ...;
+     * ObjectName namespaceMBeanName = view.getJMXNamespaceMBeanName();
+     * JMXNamespaceMBean namespaceMBean = JMX.newMBeanProxy(
+     *     view.root().getMBeanServerConnection(), namespaceMBeanName,
+     *     JMXNamespaceMBean.class);
+     * </pre>
+     *
+     * @return The name of the {@code JMXNamespaceMBean} handling the namespace
+     *         shown by this view, or {@code null}.
+     */
+    public ObjectName getJMXNamespaceMBeanName() {
+        if (parent == null)
+            return null;
+        else
+            return JMXNamespaces.getNamespaceObjectName(where);
+    }
+
+    @Override
+    public int hashCode() {
+        return where.hashCode();
+    }
+
+    /**
+     * Returns true if this object is equal to the given object.  The
+     * two objects are equal if the other object is also a {@code
+     * JMXNamespaceView} and both objects have the same {@linkplain #root root}
+     * MBeanServerConnection and the same {@linkplain #where path}.
+     * @param o the other object to compare to.
+     * @return true if both objects are equal.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o==this) return true;
+        if (! (o instanceof JMXNamespaceView)) return false;
+        if (!where.equals(((JMXNamespaceView)o).where)) return false;
+        return root().getMBeanServerConnection().equals(
+                ((JMXNamespaceView)o).root().getMBeanServerConnection());
+    }
+
+    private JMXNamespaceView makeJMXNamespaceView(final JMXNamespaceView root,
+            final JMXNamespaceView directParent, final String pathFromRoot) {
+        if (pathFromRoot.equals("")) return root;
+
+        return new JMXNamespaceView(directParent,
+                narrowToNamespace(root.getMBeanServerConnection(),
+                pathFromRoot),pathFromRoot);
+    }
+
+    private MBeanServerConnection narrowToNamespace(MBeanServerConnection root,
+            String path) {
+        if (root instanceof MBeanServer)
+            return JMXNamespaces.narrowToNamespace((MBeanServer)root, path);
+        return JMXNamespaces.narrowToNamespace(root, path);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXNamespaces.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,374 @@
+/*
+ * 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 com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.namespace.JMXNamespaceUtils;
+import com.sun.jmx.namespace.ObjectNameRouter;
+import com.sun.jmx.namespace.serial.RewritingProcessor;
+import com.sun.jmx.namespace.RoutingConnectionProxy;
+import com.sun.jmx.namespace.RoutingServerProxy;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+
+/**
+ * Static constants and utility methods to help work with
+ * JMX name spaces.  There are no instances of this class.
+ * @since 1.7
+ */
+public class JMXNamespaces {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    /** Creates a new instance of JMXNamespaces */
+    private JMXNamespaces() {
+    }
+
+    /**
+     * The name space separator. This is an alias for {@link
+     * ObjectName#NAMESPACE_SEPARATOR}.
+     **/
+    public static final String NAMESPACE_SEPARATOR =
+            ObjectName.NAMESPACE_SEPARATOR;
+    private static final int NAMESPACE_SEPARATOR_LENGTH =
+            NAMESPACE_SEPARATOR.length();
+
+
+    /**
+     * Returns a connector connected to a sub name space exposed through
+     * the parent connector.
+     * @param parent the parent connector.
+     * @param namespace the {@linkplain javax.management.namespace name space}
+     *                  to which the returned connector is
+     *                  connected.
+     * @return A connector connected to a sub name space exposed through
+     * the parent connector.
+     **/
+    public static JMXConnector narrowToNamespace(final JMXConnector parent,
+                                  final String namespace)
+        throws IOException {
+
+        return JMXNamespaceUtils.cd(parent,namespace,true);
+    }
+
+    /**
+     * Creates a new {@code MBeanServerConnection} proxy on a
+     * {@linkplain javax.management.namespace sub name space}
+     * of the given parent.
+     *
+     * @param parent The parent {@code MBeanServerConnection} that contains
+     *               the name space.
+     * @param namespace  The {@linkplain javax.management.namespace
+     *               name space} in which to narrow.
+     * @return A new {@code MBeanServerConnection} proxy that shows the content
+     *         of that name space.
+     * @throws IllegalArgumentException if the name space does not exist, or
+     *         if a proxy for that name space cannot be created.
+     */
+    public static MBeanServerConnection narrowToNamespace(
+                        MBeanServerConnection parent,
+                        String namespace) {
+        if (LOG.isLoggable(Level.FINER))
+            LOG.finer("Making MBeanServerConnection for: " +namespace);
+        return RoutingConnectionProxy.cd(parent,namespace);
+    }
+
+    /**
+     * Creates a new {@code MBeanServer} proxy on a
+     * {@linkplain javax.management.namespace sub name space}
+     * of the given parent.
+     *
+     * @param parent The parent {@code MBeanServer} that contains
+     *               the name space.
+     * @param namespace  The {@linkplain javax.management.namespace
+     *               name space} in which to narrow.
+     * @return A new {@code MBeanServer} proxy that shows the content
+     *         of that name space.
+     * @throws IllegalArgumentException if either argument is null,
+     * or the name space does not exist, or if a proxy for that name space
+     * cannot be created.
+     */
+    public static MBeanServer narrowToNamespace(MBeanServer parent,
+            String namespace) {
+        if (LOG.isLoggable(Level.FINER))
+            LOG.finer("Making NamespaceServerProxy for: " +namespace);
+        return RoutingServerProxy.cd(parent,namespace);
+    }
+
+    /**
+     * Returns an object that is the same as the given object except that
+     * any {@link ObjectName} it might contain has its domain modified.
+     * The returned object might be identical to the given object if it
+     * does not contain any {@code ObjectName} values or if none of them
+     * were modified.
+     * This method will replace a prefix ({@code toRemove}) from the path of
+     * the ObjectNames contained in {@code obj} by another prefix
+     * ({@code toAdd}).
+     * Therefore, all contained ObjectNames must have a path that start with
+     * the given {@code toRemove} prefix. If one of them doesn't, an {@link
+     * IllegalArgumentException} is thrown.
+     * <p>
+     * For instance, if {@code obj} contains the ObjectName
+     * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
+     * {@code toRemove}
+     *  is {@code x//y} this method will return a copy of {@code obj} that
+     * contains {@code v//w//z//d:k=x}.<br>
+     * On the other hand, if {@code obj} contains the ObjectName
+     * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
+     * {@code toRemove} is {@code v} this method
+     * will raise an exception, because {@code x//y//z//d:k=x} doesn't start
+     * with {@code v}
+     * </p>
+     * <p>Note: the default implementation of this method can use the
+     *   Java serialization framework to clone and replace ObjectNames in the
+     *   provided {@code obj}. It will usually fail if {@code obj} is not
+     *   Java serializable, or contains objects which are not Java
+     *   serializable.
+     * </p>
+     * @param obj    The object to deep-rewrite
+     * @param toRemove a prefix already present in contained ObjectNames.
+     *        If {@code toRemove} is the empty string {@code ""}, nothing
+     *        will be removed from the contained ObjectNames.
+     * @param toAdd the prefix that will replace (@code toRemove} in contained
+     *  ObjectNames.
+     *        If {@code toAdd} is the empty string {@code ""}, nothing
+     *        will be added to the contained ObjectNames.
+     * @return the rewritten object, or possibly {@code obj} if nothing needed
+     * to be changed.
+     * @throws IllegalArgumentException if {@code obj} couldn't be rewritten or
+     * if {@code toRemove} or {@code toAdd} is null.
+     **/
+    public static <T> T deepReplaceHeadNamespace(T obj, String toRemove, String toAdd) {
+        final RewritingProcessor processor =
+                RewritingProcessor.newRewritingProcessor(toAdd,toRemove);
+        return processor.rewriteOutput(obj);
+    }
+
+    /**
+     * Appends {@code namespace} to {@code path}.
+     * This methods appends {@code namespace} to {@code path} to obtain a
+     * a <i>full path</i>, and normalizes the result thus obtained:
+     * <ul>
+     * <li>If {@code path} is empty, the full path is
+     *     {@code namespace}.</li>
+     * <li>Otherwise, if {@code namespace} is empty,
+     *     the full path is {@code path}</li>
+     * <li>Otherwise, and this is the regular case, the full path is the
+     *     result of the concatenation of
+     *     {@code path}+{@value #NAMESPACE_SEPARATOR}+{@code namespace}</li>
+     * <li>finally, the full path is normalized: multiple consecutive
+     *     occurrences of {@value #NAMESPACE_SEPARATOR} are replaced by a
+     *     single {@value #NAMESPACE_SEPARATOR} in the result, and trailing
+     *     occurences of {@value #NAMESPACE_SEPARATOR} are removed.
+     * </li>
+     * </ul>
+     * @param path a name space path prefix
+     * @param namespace a name space name to append to the path
+     * @return a syntactically valid name space path, or "" if both parameters
+     * are null or empty.
+     * @throws IllegalArgumentException if either argument is null or ends with
+     * an odd number of {@code /} characters.
+     **/
+    public static String concat(String path, String namespace) {
+        if (path == null || namespace == null)
+            throw new IllegalArgumentException("Null argument");
+        checkTrailingSlashes(path);
+        checkTrailingSlashes(namespace);
+        final String result;
+        if (path.equals("")) result=namespace;
+        else if (namespace.equals("")) result=path;
+        else result=path+NAMESPACE_SEPARATOR+namespace;
+        return ObjectNameRouter.normalizeNamespacePath(result,false,true,false);
+    }
+
+    /**
+     * Returns a syntactically valid name space path.
+     * If the provided {@code namespace} ends with {@code "//"},
+     * recursively strips trailing {@code "//"}.  Each sequence of an
+     * even number of {@code "/"} characters is also replaced by {@code "//"},
+     * for example {@code "foo//bar////baz/////buh"} will become
+     * {@code "foo//bar//baz///buh"}.
+     *
+     * @param namespace A name space path
+     * @return {@code ""} - if the provided {@code namespace} resolves to
+     * the empty string; otherwise a syntactically valid name space string
+     * stripped of trailing and redundant {@code "//"}.
+     * @throws IllegalArgumentException if {@code namespace} is null or
+     * is not syntactically valid (e.g. it contains
+     * invalid characters like ':', or it ends with an odd
+     * number of '/').
+     */
+    public static String normalizeNamespaceName(String namespace) {
+        if (namespace == null)
+            throw new IllegalArgumentException("Null namespace");
+        final String sourcePath =
+                ObjectNameRouter.normalizeNamespacePath(namespace,false,true,false);
+        if (sourcePath.equals("")) return sourcePath;
+
+        // Will throw an IllegalArgumentException if the namespace name
+        // is not syntactically valid...
+        //
+        getNamespaceObjectName(sourcePath);
+        return sourcePath;
+    }
+
+
+    /**
+     * Return a canonical handler name for the provided {@code namespace},
+     * The handler name returned will be
+     * {@link #normalizeNamespaceName normalizeNamespaceName}{@code (namespace) +
+     * "//:type=JMXNamespace"}.
+     *
+     * @param namespace A name space path
+     * @return a canonical ObjectName for a name space handler.
+     * @see #normalizeNamespaceName
+     * @throws IllegalArgumentException if the provided
+     *          {@code namespace} is null or not valid.
+     */
+    public static ObjectName getNamespaceObjectName(String namespace) {
+        if (namespace == null || namespace.equals(""))
+            throw new IllegalArgumentException("Null or empty namespace");
+        final String sourcePath =
+                ObjectNameRouter.normalizeNamespacePath(namespace,false,
+                            true,false);
+        try {
+            return ObjectName.getInstance(sourcePath+
+                    NAMESPACE_SEPARATOR+":"+
+                    JMXNamespace.TYPE_ASSIGNMENT);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(namespace,x);
+        }
+    }
+
+    /**
+     * Returns an ObjectName pattern that can be used to query for all MBeans
+     * contained in the given name space.
+     * For instance, if {@code namespace="foo//bar"}, this method will
+     * return {@code "foo//bar//*:*"}
+     * @return an ObjectName pattern that selects all MBeans in the given
+     *         name space.
+     **/
+    public static ObjectName getWildcardFor(String namespace) {
+            return insertPath(namespace,ObjectName.WILDCARD);
+    }
+
+
+    /**
+     * Returns an ObjectName that can be used to access an MBean
+     * contained in the given name space.
+     * For instance, if {@code path="foo//bar"}, and
+     * {@code to="domain:type=Thing"} this method will
+     * return {@code "foo//bar//domain:type=Thing"}
+     * @return an ObjectName that can be used to invoke an MBean located in a
+     *         sub name space.
+     * @throws IllegalArgumentException if {@code path} ends with an
+     * odd number of {@code /} characters.
+     **/
+    public static ObjectName insertPath(String path, ObjectName to) {
+        if (path == null || to == null)
+            throw new IllegalArgumentException("Null argument");
+        checkTrailingSlashes(path);
+        try {
+            String prefix = path;
+            if (!prefix.equals("")) prefix =
+                    ObjectNameRouter.normalizeNamespacePath(
+                        prefix + NAMESPACE_SEPARATOR,false,false,false);
+            return to.withDomain(
+                    ObjectNameRouter.normalizeDomain(
+                        prefix+to.getDomain(),false));
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(path+": "+x,x);
+        }
+    }
+
+    /**
+     * Returns the normalized name space path of the name space expected to
+     * contain {@code ObjectName}.
+     * For instance, for {@code "foo//domain:type=Thing"} this will be
+     * {@code "foo"}. For {@code "//foo//bar//domain:type=Thing"} this will be
+     * {@code "foo//bar"}. For {@code //foo//bar//baz//domain:type=Thing}
+     * this will be {@code "foo//bar//baz"}. For
+     * {@code //foo//bar//baz//:type=JMXNamespace}
+     * this will be {@code "foo//bar"}.
+     *
+     * @param name an {@code ObjectName}
+     * @return the name space path of the name space that could contain such
+     *         a name. If {@code name} has no name space, returns {@code ""}.
+     * @throws IllegalArgumentException if {@code name} is null.
+     **/
+    public static String getContainingNamespace(ObjectName name) {
+        return getNormalizedPath(name,true);
+    }
+
+
+    static String getNormalizedPath(ObjectName name,
+            boolean removeLeadingSep) {
+        if (name == null)
+            throw new IllegalArgumentException("Null name");
+        String domain =
+                ObjectNameRouter.normalizeDomain(name.getDomain(),removeLeadingSep);
+        int end = domain.length();
+
+        // special case of domain part being a single '/'
+        //
+        if (domain.endsWith(NAMESPACE_SEPARATOR+"/"))
+            return domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH-1);
+
+        // special case of namespace handler
+        //
+        if (domain.endsWith(NAMESPACE_SEPARATOR))
+            domain = domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH);
+
+        int last = domain.lastIndexOf(NAMESPACE_SEPARATOR);
+        if (last < 0) return "";
+        if (last == 0) return domain;
+
+        // special case of domain part starting with '/'
+        // last=0 is not possible - we took care of this above.
+        if (domain.charAt(last-1) == '/') last--;
+
+        return domain.substring(0,last);
+    }
+
+    private static void checkTrailingSlashes(String path) {
+        int i;
+        for (i = path.length() - 1; i >= 0 && path.charAt(i) == '/'; i--)
+            continue;
+        if (path.length() - i % 2 == 0)
+            throw new IllegalArgumentException("Path ends with odd number of /");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,837 @@
+/*
+ * 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 com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.namespace.JMXNamespaceUtils;
+import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe;
+import com.sun.jmx.remote.util.EnvHelp;
+
+import java.io.IOException;
+import java.security.AccessControlException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.AttributeChangeNotification;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanPermission;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.event.EventClient;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * A {@link JMXNamespace} that will connect to a remote MBeanServer
+ * by creating a {@link javax.management.remote.JMXConnector} from a
+ * {@link javax.management.remote.JMXServiceURL}.
+ * <p>
+ * You can call {@link #connect() connect()} and {@link #close close()}
+ * several times. This MBean will emit an {@link AttributeChangeNotification}
+ * when the value of its {@link #isConnected Connected} attribute changes.
+ * </p>
+ * <p>
+ * The JMX Remote Namespace MBean is not connected until {@link
+ * #connect() connect()} is explicitly called. The usual sequence of code to
+ * create a JMX Remote Namespace is thus:
+ * </p>
+ * <pre>
+ *     final String namespace = "mynamespace";
+ *     final ObjectName name = {@link JMXNamespaces#getNamespaceObjectName
+ *       JMXNamespaces.getNamespaceObjectName(namespace)};
+ *     final JMXServiceURL remoteServerURL = .... ;
+ *     final Map<String,Object> optionsMap = .... ;
+ *     final MBeanServer masterMBeanServer = .... ;
+ *     final JMXRemoteNamespace namespaceMBean = {@link #newJMXRemoteNamespace
+ *        JMXRemoteNamespace.newJMXRemoteNamespace(remoteServerURL, optionsMap)};
+ *     masterMBeanServer.registerMBean(namespaceMBean, name);
+ *     namespaceMBean.connect();
+ *     // or: masterMBeanServer.invoke(name, {@link #connect() "connect"}, null, null);
+ * </pre>
+ * <p>
+ * The JMX Remote Namespace MBean will register for {@linkplain
+ * JMXConnectionNotification JMX Connection Notifications} with its underlying
+ * {@link JMXConnector}. When a JMX Connection Notification indicates that
+ * the underlying connection has failed, the JMX Remote Namespace MBean
+ * closes its underlying connector and switches its {@link #isConnected
+ * Connected} attribute to false, emitting an {@link
+ * AttributeChangeNotification}.
+ * </p>
+ * <p>
+ * At this point, a managing application (or an administrator connected
+ * through a management console) can attempt to reconnect the
+ * JMX Remote Namespace MBean by calling its {@link #connect() connect()} method
+ * again.
+ * </p>
+ * <p>Note that when the connection with the remote namespace fails, or when
+ *    {@link #close} is called, then any notification subscription to
+ *    MBeans registered in that namespace will be lost - unless a custom
+ *    {@linkplain javax.management.event event service} supporting connection-less
+ *    mode was used.
+ * </p>
+ * @since 1.7
+ */
+public class JMXRemoteNamespace
+        extends JMXNamespace
+        implements JMXRemoteNamespaceMBean, NotificationEmitter {
+
+    /**
+     * A logger for this class.
+     */
+    private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
+
+    private static final Logger PROBE_LOG = Logger.getLogger(
+            JmxProperties.NAMESPACE_LOGGER_NAME+".probe");
+
+
+    // This connection listener is used to listen for connection events from
+    // the underlying JMXConnector. It is used in particular to maintain the
+    // "connected" state in this MBean.
+    //
+    private static class ConnectionListener implements NotificationListener {
+        private final JMXRemoteNamespace handler;
+        private ConnectionListener(JMXRemoteNamespace handler) {
+            this.handler = handler;
+        }
+        public void handleNotification(Notification notification,
+                Object handback) {
+            if (!(notification instanceof JMXConnectionNotification))
+                return;
+            final JMXConnectionNotification cn =
+                    (JMXConnectionNotification)notification;
+            handler.checkState(this,cn,(JMXConnector)handback);
+        }
+    }
+
+    // When the JMXRemoteNamespace is originally created, it is not connected,
+    // which means that the source MBeanServer should be one that throws
+    // exceptions for most methods.  When it is subsequently connected,
+    // the methods should be forwarded to the MBeanServerConnection.
+    // We handle this using MBeanServerConnectionWrapper.  The
+    // MBeanServerConnection that is supplied to the constructor of
+    // MBeanServerConnectionWrapper is ignored (and in fact it is null)
+    // because the one that is actually used is the one supplied by the
+    // override of getMBeanServerConnection().
+    private static class JMXRemoteNamespaceDelegate
+            extends MBeanServerConnectionWrapper
+            implements DynamicProbe {
+        private volatile JMXRemoteNamespace parent=null;
+
+        JMXRemoteNamespaceDelegate() {
+            super(null,null);
+        }
+        @Override
+        public MBeanServerConnection getMBeanServerConnection() {
+            return parent.getMBeanServerConnection();
+        }
+        @Override
+        public ClassLoader getDefaultClassLoader() {
+            return parent.getDefaultClassLoader();
+        }
+
+        // Because this class is instantiated in the super() call from the
+        // constructor of JMXRemoteNamespace, it cannot be an inner class.
+        // This method achieves the effect that an inner class would have
+        // had, of giving the class a reference to the outer "this".
+        synchronized void initParentOnce(JMXRemoteNamespace parent) {
+            if (this.parent != null)
+                throw new UnsupportedOperationException("parent already set");
+            this.parent=parent;
+
+        }
+
+        public boolean isProbeRequested() {
+            return this.parent.isProbeRequested();
+        }
+    }
+
+    private static final MBeanNotificationInfo connectNotification =
+        new MBeanNotificationInfo(new String[] {
+            AttributeChangeNotification.ATTRIBUTE_CHANGE},
+            "Connected",
+            "Emitted when the Connected state of this object changes");
+
+    private static long seqNumber=0;
+
+    private final NotificationBroadcasterSupport broadcaster;
+    private final ConnectionListener listener;
+    private final JMXServiceURL jmxURL;
+    private final Map<String,?> optionsMap;
+
+    private volatile MBeanServerConnection server = null;
+    private volatile JMXConnector conn = null;
+    private volatile ClassLoader defaultClassLoader = null;
+    private volatile boolean probed;
+
+    /**
+     * Creates a new instance of {@code JMXRemoteNamespace}.
+     * <p>
+     * This constructor is provided for subclasses.
+     * To create a new instance of {@code JMXRemoteNamespace} call
+     * {@link #newJMXRemoteNamespace
+     *  JMXRemoteNamespace.newJMXRemoteNamespace(sourceURL, optionsMap)}.
+     * </p>
+     * @param sourceURL a JMX service URL that can be used to {@linkplain
+     *        #connect() connect} to the
+     *        source MBean Server. The source MBean Server is the remote
+     *        MBean Server which contains the MBeans that will be mirrored
+     *        in this namespace.
+     * @param optionsMap the options map that will be passed to the
+     *        {@link JMXConnectorFactory} when {@linkplain
+     *        JMXConnectorFactory#newJMXConnector creating} the
+     *        {@link JMXConnector} used to {@linkplain #connect() connect}
+     *        to the remote source MBean Server.  Can be null, which is
+     *        equivalent to an empty map.
+     * @see #newJMXRemoteNamespace JMXRemoteNamespace.newJMXRemoteNamespace
+     * @see #connect
+     */
+    protected JMXRemoteNamespace(JMXServiceURL sourceURL,
+            Map<String,?> optionsMap) {
+         super(new JMXRemoteNamespaceDelegate());
+        ((JMXRemoteNamespaceDelegate)super.getSourceServer()).
+                initParentOnce(this);
+
+        // URL must not be null.
+        this.jmxURL     = JMXNamespaceUtils.checkNonNull(sourceURL,"url");
+        this.broadcaster =
+            new NotificationBroadcasterSupport(connectNotification);
+
+        // handles options
+        this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap);
+
+        // handles (dis)connection events
+        this.listener = new ConnectionListener(this);
+
+        // XXX TODO: remove the probe, or simplify it.
+        this.probed = false;
+    }
+
+   /**
+    * Returns the {@code JMXServiceURL} that is (or will be) used to
+    * connect to the remote name space. <p>
+    * @see #connect
+    * @return The {@code JMXServiceURL} used to connect to the remote
+    *         name space.
+    */
+    public JMXServiceURL getJMXServiceURL() {
+        return jmxURL;
+    }
+
+    /**
+    * In this class, this method never returns {@code null}, and the
+    * address returned is the {@link  #getJMXServiceURL JMXServiceURL}
+    * that is used by  this object to {@linkplain #connect} to the remote
+    * name space. <p>
+    * This behaviour might be overriden by subclasses, if needed.
+    * For instance, a subclass might want to return {@code null} if it
+    * doesn't want to expose that JMXServiceURL.
+    */
+    public JMXServiceURL getAddress() {
+        return getJMXServiceURL();
+    }
+
+    private Map<String,?> getEnvMap() {
+        return optionsMap;
+    }
+
+    boolean isProbeRequested() {
+        return probed==false;
+    }
+
+    public void addNotificationListener(NotificationListener listener,
+            NotificationFilter filter, Object handback) {
+        broadcaster.addNotificationListener(listener, filter, handback);
+    }
+
+    /**
+     * A subclass that needs to send its own notifications must override
+     * this method in order to return an {@link MBeanNotificationInfo
+     * MBeanNotificationInfo[]} array containing both its own notification
+     * infos and the notification infos of its super class. <p>
+     * The implementation should probably look like:
+     * <pre>
+     *      final MBeanNotificationInfo[] myOwnNotifs = { .... };
+     *      final MBeanNotificationInfo[] parentNotifs =
+     *            super.getNotificationInfo();
+     *      final Set<MBeanNotificationInfo> mergedResult =
+     *            new HashSet<MBeanNotificationInfo>();
+     *      mergedResult.addAll(Arrays.asList(myOwnNotifs));
+     *      mergedResult.addAll(Arrays.asList(parentNotifs));
+     *      return mergeResult.toArray(
+     *             new MBeanNotificationInfo[mergedResult.size()]);
+     * </pre>
+     */
+    public MBeanNotificationInfo[] getNotificationInfo() {
+        return broadcaster.getNotificationInfo();
+    }
+
+    public void removeNotificationListener(NotificationListener listener)
+    throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener);
+    }
+
+    public void removeNotificationListener(NotificationListener listener,
+            NotificationFilter filter, Object handback)
+            throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener, filter, handback);
+    }
+
+    private static synchronized long getNextSeqNumber() {
+        return seqNumber++;
+    }
+
+
+    /**
+     * Sends a notification to registered listeners. Before the notification
+     * is sent, the following steps are performed:
+     * <ul><li>
+     * If {@code n.getSequenceNumber() <= 0} set it to the next available
+     * sequence number.</li>
+     * <li>If {@code n.getSource() == null}, set it to the value returned by {@link
+     * #getObjectName getObjectName()}.
+     * </li></ul>
+     * <p>This method can be called by subclasses in order to send their own
+     *    notifications.
+     *    In that case, these subclasses might also need to override
+     *    {@link #getNotificationInfo} in order to declare their own
+     *    {@linkplain MBeanNotificationInfo notification types}.
+     * </p>
+     * @param n The notification to send to registered listeners.
+     * @see javax.management.NotificationBroadcasterSupport
+     * @see #getNotificationInfo
+     **/
+    protected void sendNotification(Notification n) {
+        if (n.getSequenceNumber()<=0)
+            n.setSequenceNumber(getNextSeqNumber());
+        if (n.getSource()==null)
+            n.setSource(getObjectName());
+        broadcaster.sendNotification(n);
+    }
+
+    private void checkState(ConnectionListener listener,
+                            JMXConnectionNotification cn,
+                            JMXConnector emittingConnector) {
+
+        // Due to the asynchronous handling of notifications, it is
+        // possible that this method is called for a JMXConnector
+        // (or connection) which is already closed and replaced by a newer
+        // one.
+        //
+        // This method attempts to determine the real state of the
+        // connection - which might be different from what the notification
+        // says.
+        //
+        // This is quite complex logic - because we try not to hold any
+        // lock while evaluating the true value of the connected state,
+        // while anyone might also call close() or connect() from a
+        // different thread.
+        //
+        // The method switchConnection() (called from here too) also has the
+        // same kind of complex logic.
+        //
+        // We use the JMXConnector has a handback to the notification listener
+        // (emittingConnector) in order to be able to determine whether the
+        // notification concerns the current connector in use, or an older
+        // one.
+        //
+        boolean remove = false;
+
+        // whether the emittingConnector is already 'removed'
+        synchronized (this) {
+            if (this.conn != emittingConnector ||
+                    JMXConnectionNotification.FAILED.equals(cn.getType()))
+                remove = true;
+        }
+
+        // We need to unregister our listener from this 'removed' connector.
+        // This is the only place where we remove the listener.
+        //
+        if (remove) {
+            try {
+                // This may fail if the connector is already closed.
+                // But better unregister anyway...
+                //
+                emittingConnector.removeConnectionNotificationListener(
+                        listener,null,
+                        emittingConnector);
+            } catch (Exception x) {
+                LOG.log(Level.FINE,
+                        "Failed to unregister connection listener"+x);
+                LOG.log(Level.FINEST,
+                        "Failed to unregister connection listener",x);
+            }
+            try {
+                // This may fail if the connector is already closed.
+                // But better call close twice and get an exception than
+                // leaking...
+                //
+                emittingConnector.close();
+            } catch (Exception x) {
+                LOG.log(Level.FINEST,
+                        "Failed to close old connector " +
+                        "(failure was expected): "+x);
+            }
+        }
+
+        // Now we checked whether our current connector is still alive.
+        //
+        boolean closed = false;
+        final JMXConnector thisconn = this.conn;
+        try {
+            if (thisconn != null)
+                thisconn.getConnectionId();
+        } catch (IOException x) {
+            LOG.finest("Connector already closed: "+x);
+            closed = true;
+        }
+
+        // We got an IOException - the connector is not connected.
+        // Need to forget it and switch our state to closed.
+        //
+        if (closed) {
+            switchConnection(thisconn,null,null);
+            try {
+                // Usually this will fail... Better call close twice
+                // and get an exception than leaking...
+                //
+                if (thisconn != emittingConnector || !remove)
+                    thisconn.close();
+            } catch (IOException x) {
+                LOG.log(Level.FINEST,
+                        "Failed to close connector (failure was expected): "
+                        +x);
+            }
+        }
+    }
+
+    private final void switchConnection(JMXConnector oldc,
+                                   JMXConnector newc,
+                                   MBeanServerConnection mbs) {
+        boolean connect = false;
+        boolean close   = false;
+        synchronized (this) {
+            if (oldc != conn) {
+                if (newc != null) {
+                    try {
+                        newc.close();
+                    } catch (IOException x) {
+                        LOG.log(Level.FINEST,
+                                "Failed to close connector",x);
+                    }
+                }
+                return;
+            }
+            if (conn == null && newc != null) connect=true;
+            if (newc == null && conn != null) close = true;
+            conn = newc;
+            server = mbs;
+        }
+        if (connect || close) {
+            boolean oldstate = close;
+            boolean newstate = connect;
+            final ObjectName myName = getObjectName();
+
+            // In the uncommon case where the MBean is connected before
+            // being registered, myName can be null...
+            // If myName is null - we use 'this' as the source instead...
+            //
+            final Object source = (myName==null)?this:myName;
+            final AttributeChangeNotification acn =
+                    new AttributeChangeNotification(source,
+                    getNextSeqNumber(),System.currentTimeMillis(),
+                    String.valueOf(source)+
+                    (newstate?" connected":" closed"),
+                    "Connected",
+                    "boolean",
+                    Boolean.valueOf(oldstate),
+                    Boolean.valueOf(newstate));
+            sendNotification(acn);
+        }
+    }
+
+    private void closeall(JMXConnector... a) {
+        for (JMXConnector c : a) {
+            try {
+                if (c != null) c.close();
+            } catch (Exception x) {
+                // OK: we're gonna throw the original exception later.
+                LOG.finest("Ignoring exception when closing connector: "+x);
+            }
+        }
+    }
+
+    JMXConnector connect(JMXServiceURL url, Map<String,?> env)
+            throws IOException {
+        final JMXConnector c = newJMXConnector(jmxURL, env);
+        c.connect(env);
+        return c;
+    }
+
+    /**
+     * Creates a new JMXConnector with the specified {@code url} and
+     * {@code env} options map.
+     * <p>
+     * This method first calls {@link JMXConnectorFactory#newJMXConnector
+     * JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
+     * JMX connector, and returns that.
+     * </p>
+     * <p>
+     * A subclass of {@link JMXRemoteNamespace} can provide an implementation
+     * that connects to a  sub namespace of the remote server by subclassing
+     * this class in the following way:
+     * <pre>
+     * class JMXRemoteSubNamespace extends JMXRemoteNamespace {
+     *    private final String subnamespace;
+     *    JMXRemoteSubNamespace(JMXServiceURL url,
+     *              Map{@code <String,?>} env, String subnamespace) {
+     *        super(url,options);
+     *        this.subnamespace = subnamespace;
+     *    }
+     *    protected JMXConnector newJMXConnector(JMXServiceURL url,
+     *              Map<String,?> env) throws IOException {
+     *        final JMXConnector inner = super.newJMXConnector(url,env);
+     *        return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String)
+     *               JMXNamespaces.narrowToNamespace(inner,subnamespace)};
+     *    }
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * Some connectors, like the JMXMP connector server defined by the
+     * version 1.2 of the JMX API may not have been upgraded to use the
+     * new {@linkplain javax.management.event Event Service} defined in this
+     * version of the JMX API.
+     * <p>
+     * In that case, and if the remote server to which this JMXRemoteNamespace
+     * connects also contains namespaces, it may be necessary to configure
+     * explicitly an {@linkplain
+     * javax.management.event.EventClientDelegate#newForwarder()
+     * Event Client Forwarder} on the remote server side, and to force the use
+     * of an {@link EventClient} on this client side.
+     * <br>
+     * A subclass of {@link JMXRemoteNamespace} can provide an implementation
+     * of {@code newJMXConnector} that will force notification subscriptions
+     * to flow through an {@link EventClient} over a legacy protocol by
+     * overriding this method in the following way:
+     * </p>
+     * <pre>
+     * class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
+     *    JMXRemoteSubNamespaceConnector(JMXServiceURL url,
+     *              Map<String,?> env) {
+     *        super(url,options);
+     *    }
+     *    protected JMXConnector newJMXConnector(JMXServiceURL url,
+     *              Map<String,?> env) throws IOException {
+     *        final JMXConnector inner = super.newJMXConnector(url,env);
+     *        return {@link EventClient#withEventClient(
+     *                JMXConnector) EventClient.withEventClient(inner)};
+     *    }
+     * }
+     * </pre>
+     * <p>
+     * Note that the remote server also needs to provide an {@link
+     * javax.management.event.EventClientDelegateMBean}: only configuring
+     * the client side (this object) is not enough.<br>
+     * In summary, this technique should be used if the remote server
+     * supports JMX namespaces, but uses a JMX Connector Server whose
+     * implementation does not transparently use the new Event Service
+     * (as would be the case with the JMXMPConnectorServer implementation
+     * from the reference implementation of the JMX Remote API 1.0
+     * specification).
+     * </p>
+     * @param url  The JMXServiceURL of the remote server.
+     * @param optionsMap An unmodifiable options map that will be passed to the
+     *        {@link JMXConnectorFactory} when {@linkplain
+     *        JMXConnectorFactory#newJMXConnector creating} the
+     *        {@link JMXConnector} that can connect to the remote source
+     *        MBean Server.
+     * @return An unconnected JMXConnector to use to connect to the remote
+     *         server
+     * @throws java.io.IOException if the connector could not be created.
+     * @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
+     * @see #JMXRemoteNamespace
+     */
+    protected JMXConnector newJMXConnector(JMXServiceURL url,
+            Map<String,?> optionsMap) throws IOException {
+        final JMXConnector c =
+                JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
+// TODO: uncomment this when contexts are added
+//        return ClientContext.withDynamicContext(c);
+        return c;
+    }
+
+    public void connect() throws IOException {
+        if (conn != null) {
+            try {
+               // This is much too fragile. It must go away!
+               PROBE_LOG.finest("Probing again...");
+               triggerProbe(getMBeanServerConnection());
+            } catch(Exception x) {
+                close();
+                Throwable cause = x;
+                // if the cause is a security exception - rethrows it...
+                while (cause != null) {
+                    if (cause instanceof SecurityException)
+                        throw (SecurityException) cause;
+                    cause = cause.getCause();
+                }
+                throw new IOException("connection failed: cycle?",x);
+            }
+        }
+        LOG.fine("connecting...");
+        // TODO remove these traces
+        // System.err.println(getInitParameter()+" connecting");
+        final Map<String,Object> env =
+                new HashMap<String,Object>(getEnvMap());
+        try {
+            // XXX: We should probably document this...
+            // This allows to specify a loader name - which will be
+            // retrieved from the paret MBeanServer.
+            defaultClassLoader =
+                EnvHelp.resolveServerClassLoader(env,getMBeanServer());
+        } catch (InstanceNotFoundException x) {
+            final IOException io =
+                    new IOException("ClassLoader not found");
+            io.initCause(x);
+            throw io;
+        }
+        env.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,defaultClassLoader);
+        final JMXServiceURL url = getJMXServiceURL();
+        final JMXConnector aconn = connect(url,env);
+        final MBeanServerConnection msc;
+        try {
+            msc = aconn.getMBeanServerConnection();
+            aconn.addConnectionNotificationListener(listener,null,aconn);
+        } catch (IOException io) {
+            closeall(aconn);
+            throw io;
+        } catch (RuntimeException x) {
+            closeall(aconn);
+            throw x;
+        }
+
+
+        // XXX Revisit here
+        // Note from the author: This business of switching connection is
+        // incredibly complex. Isn't there any means to simplify it?
+        //
+        switchConnection(conn,aconn,msc);
+        try {
+           triggerProbe(msc);
+        } catch(Exception x) {
+            close();
+            Throwable cause = x;
+            // if the cause is a security exception - rethrows it...
+            while (cause != null) {
+                if (cause instanceof SecurityException)
+                    throw (SecurityException) cause;
+                cause = cause.getCause();
+            }
+            throw new IOException("connection failed: cycle?",x);
+        }
+        LOG.fine("connected.");
+    }
+
+    // If this is a self-linking namespace, this method should trigger
+    // the emission of a probe in the wrapping NamespaceInterceptor.
+    // The first call to source() in the wrapping NamespaceInterceptor
+    // causes the emission of the probe.
+    //
+    // Note: the MBeanServer returned by getSourceServer
+    //       (our private JMXRemoteNamespaceDelegate inner class)
+    //       implements a sun private interface (DynamicProbe) which is
+    //       used by the NamespaceInterceptor to determine whether it should
+    //       send a probe or not.
+    //       We needed this interface here because the NamespaceInterceptor
+    //       has otherwise no means to knows that this object has just
+    //       connected, and that a new probe should be sent.
+    //
+    // Probes work this way: the NamespaceInterceptor sets a flag and sends
+    // a queryNames() request. If a queryNames() request comes in when the flag
+    // is on, then it deduces that there is a self-linking loop - and instead
+    // of calling queryNames() on the JMXNamespace (which would cause the
+    // loop to go on) it breaks the recursion by returning the probe ObjectName.
+    // If the NamespaceInterceptor receives the probe ObjectName as result of
+    // its original queryNames() it knows that it has been looping back on
+    // itslef and throws an Exception - which will be raised through this
+    // method, thus preventing the connection to be established...
+    //
+    // More info in the com.sun.jmx.namespace.NamespaceInterceptor class
+    //
+    // XXX: TODO this probe thing is way too complex and fragile.
+    //      This *must* go away or be replaced by something simpler.
+    //      ideas are welcomed.
+    //
+    private void triggerProbe(final MBeanServerConnection msc)
+            throws MalformedObjectNameException, IOException {
+        // Query Pattern that we will send through the source server in order
+        // to detect self-linking namespaces.
+        //
+        //
+        final ObjectName pattern;
+        pattern = ObjectName.getInstance("*" +
+                JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
+                JMXNamespace.TYPE_ASSIGNMENT);
+        probed = false;
+        try {
+            msc.queryNames(pattern, null);
+            probed = true;
+        } catch (AccessControlException x) {
+            // if we have an MBeanPermission missing then do nothing...
+            if (!(x.getPermission() instanceof MBeanPermission))
+                throw x;
+            PROBE_LOG.finer("Can't check for cycles: " + x);
+            probed = false; // no need to do it again...
+        }
+    }
+
+    public void close() throws IOException {
+        if (conn == null) return;
+        LOG.fine("closing...");
+        // System.err.println(toString()+": closing...");
+        conn.close();
+        // System.err.println(toString()+": connector closed");
+        switchConnection(conn,null,null);
+        LOG.fine("closed.");
+        // System.err.println(toString()+": closed");
+    }
+
+    MBeanServerConnection getMBeanServerConnection() {
+        if (conn == null)
+            throw newRuntimeIOException("getMBeanServerConnection: not connected");
+        return server;
+    }
+
+    // Better than throwing UndeclaredThrowableException ...
+    private RuntimeException newRuntimeIOException(String msg) {
+        final IllegalStateException illegal = new IllegalStateException(msg);
+        return Util.newRuntimeIOException(new IOException(msg,illegal));
+    }
+
+    /**
+     * Returns the default class loader used by the underlying
+     * {@link JMXConnector}.
+     * @return the default class loader used when communicating with the
+     *         remote source MBean server.
+     **/
+    ClassLoader getDefaultClassLoader() {
+        if (conn == null)
+            throw newRuntimeIOException("getMBeanServerConnection: not connected");
+        return defaultClassLoader;
+    }
+
+    public boolean isConnected() {
+        // This is a pleonasm
+        return (conn != null) && (server != null);
+    }
+
+
+    /**
+     * This name space handler will automatically {@link #close} its
+     * connection with the remote source in {@code preDeregister}.
+     **/
+    @Override
+    public void preDeregister() throws Exception {
+        try {
+            close();
+        } catch (IOException x) {
+            LOG.fine("Failed to close properly - exception ignored: " + x);
+            LOG.log(Level.FINEST,
+                    "Failed to close properly - exception ignored",x);
+        }
+        super.preDeregister();
+    }
+
+   /**
+    * This method calls {@link
+    * javax.management.MBeanServerConnection#getMBeanCount
+    * getMBeanCount()} on the remote namespace.
+    * @throws java.io.IOException if an {@link IOException} is raised when
+    *         communicating with the remote source namespace.
+    */
+    @Override
+    public Integer getMBeanCount() throws IOException {
+        return getMBeanServerConnection().getMBeanCount();
+    }
+
+   /**
+    * This method returns the result of calling {@link
+    * javax.management.MBeanServerConnection#getDomains
+    * getDomains()} on the remote namespace.
+    * @throws java.io.IOException if an {@link IOException} is raised when
+    *         communicating with the remote source namespace.
+    */
+    @Override
+   public String[] getDomains() throws IOException {
+       return getMBeanServerConnection().getDomains();
+    }
+
+   /**
+    * This method returns the result of calling {@link
+    * javax.management.MBeanServerConnection#getDefaultDomain
+    * getDefaultDomain()} on the remote namespace.
+    * @throws java.io.IOException if an {@link IOException} is raised when
+    *         communicating with the remote source namespace.
+    */
+    @Override
+    public String getDefaultDomain() throws IOException {
+        return getMBeanServerConnection().getDefaultDomain();
+    }
+
+    /**
+     * Creates a new instance of {@code JMXRemoteNamespace}.
+     * @param sourceURL a JMX service URL that can be used to connect to the
+     *        source MBean Server. The source MBean Server is the remote
+     *        MBean Server which contains the MBeans that will be mirrored
+     *        in this namespace.
+     * @param optionsMap An options map that will be passed to the
+     *        {@link JMXConnectorFactory} when {@linkplain
+     *        JMXConnectorFactory#newJMXConnector creating} the
+     *        {@link JMXConnector} used to connect to the remote source
+     *        MBean Server.  Can be null, which is equivalent to an empty map.
+     * @see #JMXRemoteNamespace JMXRemoteNamespace(sourceURL,optionsMap)
+     * @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
+     */
+     public static JMXRemoteNamespace newJMXRemoteNamespace(
+             JMXServiceURL sourceURL,
+             Map<String,?> optionsMap) {
+         return new JMXRemoteNamespace(sourceURL, optionsMap);
+     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,96 @@
+/*
+ * 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.remote.JMXServiceURL;
+
+/**
+ * A {@link JMXNamespaceMBean} that will connect to a remote MBeanServer
+ * by creating a {@link javax.management.remote.JMXConnector} from a
+ * {@link javax.management.remote.JMXServiceURL}.
+ * You can call {@link #connect connect()} and {@link #close close()}
+ * several times.
+ * @since 1.7
+ */
+public interface JMXRemoteNamespaceMBean
+        extends JMXNamespaceMBean {
+
+    /**
+     * Connects to the underlying remote source name space, if not already
+     * {@link #isConnected connected}.
+     * If connected, do nothing. Otherwise, creates a new connector from the
+     * {@link javax.management.remote.JMXServiceURL JMXServiceURL} provided at
+     * creation time, and connects to the remote source name space.
+     * <p>
+     * The source MBeans will not appear in the target name space until the
+     * JMXRemoteNamespaceMBean is connected.
+     * </p><p>
+     * It is possible to call {@code connect()}, {@link #close close()}, and
+     * {@code connect()} again.
+     * However, closing the connection with the remote name space may cause
+     * notification listeners to be lost, unless the client explicitly uses
+     * the new {@linkplain javax.management.event JMX event service}.
+     * </p><p>
+     * @throws IOException if connection to the remote source name space fails.
+     * @see #isConnected isConnected
+     **/
+    public void connect()
+        throws IOException;
+
+    /**
+     * Closes the connection with the remote source name space.
+     * If the connection is already closed, do nothing.
+     * Otherwise, closes the underlying {@link
+     * javax.management.remote.JMXConnector}.
+     * <p>Once closed, it is possible to reopen the connection by
+     * calling {@link #connect connect}.
+     * </p>
+     * @throws IOException if the connection to the remote source name space
+     *         can't be closed properly.
+     * @see #isConnected isConnected
+     **/
+    public void close()
+        throws IOException;
+
+    /**
+     * Tells whether the connection to the remote source name space is opened.
+     * @see #connect connect
+     * @see #close close
+     * @return {@code true} if connected.
+     **/
+    public boolean isConnected();
+
+    /**
+     * Returns the {@link JMXServiceURL} address that points to the remote name
+     * space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean},
+     * if available.
+     * @return The {@link JMXServiceURL} address that points to the remote name
+     * space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean},
+     * or {@code null}.
+     */
+    public JMXServiceURL getAddress();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,703 @@
+/*
+ * 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 com.sun.jmx.mbeanserver.Util;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.security.AccessController;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * <p>An object of this class implements the MBeanServer interface
+ * and, for each of its methods forwards the request to a wrapped
+ * {@link MBeanServerConnection} object.
+ * Some methods of the {@link MBeanServer} interface do not have
+ * any equivalent in {@link MBeanServerConnection}. In that case, an
+ * {@link UnsupportedOperationException} will be thrown.
+ *
+ * <p>A typical use of this class is to apply a {@link QueryExp} object locally,
+ * on an MBean that resides in a remote MBeanServer. Since an
+ * MBeanServerConnection is not an MBeanServer, it cannot be passed
+ * to the <code>setMBeanServer()</code> method of the {@link QueryExp}
+ * object. However, this object can.</p>
+ *
+ * @since 1.7
+ */
+public class MBeanServerConnectionWrapper
+        implements MBeanServer {
+
+    private final MBeanServerConnection wrapped;
+    private final ClassLoader defaultCl;
+
+    /**
+     * Construct a new object that implements {@link MBeanServer} by
+     * forwarding its methods to the given {@link MBeanServerConnection}.
+     * This constructor is equivalent to {@link #MBeanServerConnectionWrapper(
+     * MBeanServerConnection, ClassLoader) MBeanServerConnectionWrapper(wrapped,
+     * null)}.
+     *
+     * @param wrapped the {@link MBeanServerConnection} to which methods
+     * are to be forwarded.  This parameter can be null, in which case the
+     * {@code MBeanServerConnection} will typically be supplied by overriding
+     * {@link #getMBeanServerConnection}.
+     */
+    public MBeanServerConnectionWrapper(MBeanServerConnection wrapped) {
+        this(wrapped, null);
+    }
+
+    /**
+     * Construct a new object that implements {@link MBeanServer} by
+     * forwarding its methods to the given {@link MBeanServerConnection}.
+     * The {@code defaultCl} parameter specifies the value to be returned
+     * by {@link #getDefaultClassLoader}.  A null value is equivalent to
+     * {@link Thread#getContextClassLoader()}.
+     *
+     * @param wrapped the {@link MBeanServerConnection} to which methods
+     * are to be forwarded.  This parameter can be null, in which case the
+     * {@code MBeanServerConnection} will typically be supplied by overriding
+     * {@link #getMBeanServerConnection}.
+     * @param defaultCl the value to be returned by {@link
+     * #getDefaultClassLoader}.  A null value is equivalent to the current
+     * thread's {@linkplain Thread#getContextClassLoader()}.
+     */
+    public MBeanServerConnectionWrapper(MBeanServerConnection wrapped,
+            ClassLoader defaultCl) {
+        this.wrapped = wrapped;
+        this.defaultCl = (defaultCl == null) ?
+            Thread.currentThread().getContextClassLoader() : defaultCl;
+    }
+
+    /**
+     * Returns an MBeanServerConnection. This method is called each time
+     * an operation must be invoked on the underlying MBeanServerConnection.
+     * The default implementation returns the MBeanServerConnection that
+     * was supplied to the constructor of this MBeanServerConnectionWrapper.
+     **/
+    protected MBeanServerConnection getMBeanServerConnection() {
+        return wrapped;
+    }
+
+    /**
+     * Returns the default class loader passed to the constructor.  If the
+     * value passed was null, then the returned value will be the
+     * {@linkplain Thread#getContextClassLoader() context class loader} at the
+     * time this object was constructed.
+     *
+     * @return the ClassLoader that was passed to the constructor.
+     **/
+    public ClassLoader getDefaultClassLoader() {
+        return defaultCl;
+    }
+
+    /**
+     * <p>This method is called each time an IOException is raised when
+     * trying to forward an operation to the underlying
+     * MBeanServerConnection, as a result of calling
+     * {@link #getMBeanServerConnection()} or as a result of invoking the
+     * operation on the returned connection.  Since the methods in
+     * {@link MBeanServer} are not declared to throw {@code IOException},
+     * this method must return a {@code RuntimeException} to be thrown
+     * instead.  Typically, the original {@code IOException} will be in the
+     * {@linkplain Throwable#getCause() cause chain} of the {@code
+     * RuntimeException}.</p>
+     *
+     * <p>Subclasses may redefine this method if they need to perform any
+     * specific handling of IOException (logging etc...).</p>
+     *
+     * @param x The raised IOException.
+     * @param method The name of the method in which the exception was
+     *        raised. This is one of the methods of the MBeanServer
+     *        interface.
+     *
+     * @return A RuntimeException that should be thrown by the caller.
+     *         In this default implementation, this is a
+     *         {@link RuntimeException} wrapping <var>x</var>.
+     **/
+    protected RuntimeException wrapIOException(IOException x, String method) {
+        return Util.newRuntimeIOException(x);
+    }
+
+    // Take care of getMBeanServerConnection returning null.
+    //
+    private synchronized MBeanServerConnection connection()
+        throws IOException {
+        final MBeanServerConnection c = getMBeanServerConnection();
+        if (c == null)
+            throw new IOException("MBeanServerConnection unavailable");
+        return c;
+    }
+
+    //--------------------------------------------
+    //--------------------------------------------
+    //
+    // Implementation of the MBeanServer interface
+    //
+    //--------------------------------------------
+    //--------------------------------------------
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void addNotificationListener(ObjectName name,
+                                        NotificationListener listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+        throws InstanceNotFoundException {
+        try {
+            connection().addNotificationListener(name, listener,
+                                                 filter, handback);
+        } catch (IOException x) {
+            throw wrapIOException(x,"addNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void addNotificationListener(ObjectName name,
+                                        ObjectName listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+        throws InstanceNotFoundException {
+        try {
+            connection().addNotificationListener(name, listener,
+                                                 filter, handback);
+        } catch (IOException x) {
+            throw wrapIOException(x,"addNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public ObjectInstance createMBean(String className, ObjectName name)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException {
+        try {
+            return connection().createMBean(className, name);
+        } catch (IOException x) {
+            throw wrapIOException(x,"createMBean");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public ObjectInstance createMBean(String className, ObjectName name,
+                                      Object params[], String signature[])
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException {
+        try {
+            return connection().createMBean(className, name,
+                                            params, signature);
+        } catch (IOException x) {
+            throw wrapIOException(x,"createMBean");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      ObjectName loaderName)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException {
+        try {
+            return connection().createMBean(className, name, loaderName);
+        } catch (IOException x) {
+            throw wrapIOException(x,"createMBean");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      ObjectName loaderName,
+                                      Object params[],
+                                      String signature[])
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException {
+        try {
+            return connection().createMBean(className, name, loaderName,
+                                            params, signature);
+        } catch (IOException x) {
+            throw wrapIOException(x,"createMBean");
+        }
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     * @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[])
+     *                 MBeanServer}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+        throws InstanceNotFoundException, OperationsException {
+        throw new UnsupportedOperationException("deserialize");
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     * @deprecated see {@link MBeanServer#deserialize(String,byte[])
+     *                 MBeanServer}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+        throws OperationsException, ReflectionException {
+        throw new UnsupportedOperationException("deserialize");
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     * @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[])
+     *                 MBeanServer}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+                                         ObjectName loaderName,
+                                         byte[] data)
+        throws
+        InstanceNotFoundException,
+        OperationsException,
+        ReflectionException {
+        throw new UnsupportedOperationException("deserialize");
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public Object getAttribute(ObjectName name, String attribute)
+        throws
+        MBeanException,
+        AttributeNotFoundException,
+        InstanceNotFoundException,
+        ReflectionException {
+        try {
+            return connection().getAttribute(name, attribute);
+        } catch (IOException x) {
+            throw wrapIOException(x,"getAttribute");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public AttributeList getAttributes(ObjectName name, String[] attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        try {
+            return connection().getAttributes(name, attributes);
+        } catch (IOException x) {
+            throw wrapIOException(x,"getAttributes");
+        }
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public ClassLoader getClassLoader(ObjectName loaderName)
+        throws InstanceNotFoundException {
+        throw new UnsupportedOperationException("getClassLoader");
+    }
+
+    /**
+     * Returns the {@linkplain #getDefaultClassLoader() default class loader}.
+     * This behavior can be changed by subclasses.
+     */
+    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+        throws InstanceNotFoundException {
+        return getDefaultClassLoader();
+    }
+
+    /**
+     * <p>Returns a {@link ClassLoaderRepository} based on the class loader
+     * returned by {@link #getDefaultClassLoader()}.</p>
+     * @return a {@link ClassLoaderRepository} that contains a single
+     *         class loader, returned by {@link #getDefaultClassLoader()}.
+     **/
+    public ClassLoaderRepository getClassLoaderRepository() {
+        // We return a new ClassLoaderRepository each time this method is
+        // called. This is by design, because there's no guarantee that
+        // getDefaultClassLoader() will always return the same class loader.
+        return Util.getSingleClassLoaderRepository(getDefaultClassLoader());
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public String getDefaultDomain() {
+        try {
+            return connection().getDefaultDomain();
+        } catch (IOException x) {
+            throw wrapIOException(x,"getDefaultDomain");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public String[] getDomains() {
+        try {
+            return connection().getDomains();
+        } catch (IOException x) {
+            throw wrapIOException(x,"getDomains");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public Integer getMBeanCount() {
+        try {
+            return connection().getMBeanCount();
+        } catch (IOException x) {
+            throw wrapIOException(x,"getMBeanCount");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public MBeanInfo getMBeanInfo(ObjectName name)
+        throws
+        InstanceNotFoundException,
+        IntrospectionException,
+        ReflectionException {
+        try {
+            return connection().getMBeanInfo(name);
+        } catch (IOException x) {
+            throw wrapIOException(x,"getMBeanInfo");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public ObjectInstance getObjectInstance(ObjectName name)
+        throws InstanceNotFoundException {
+        try {
+            return connection().getObjectInstance(name);
+        } catch (IOException x) {
+            throw wrapIOException(x,"getObjectInstance");
+        }
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public Object instantiate(String className)
+        throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("instantiate");
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public Object instantiate(String className,
+                              Object params[],
+                              String signature[])
+        throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("instantiate");
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public Object instantiate(String className, ObjectName loaderName)
+        throws ReflectionException, MBeanException,
+               InstanceNotFoundException {
+        throw new UnsupportedOperationException("instantiate");
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public Object instantiate(String className, ObjectName loaderName,
+                              Object params[], String signature[])
+        throws ReflectionException, MBeanException,
+               InstanceNotFoundException {
+        throw new UnsupportedOperationException("instantiate");
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public Object invoke(ObjectName name, String operationName,
+                         Object params[], String signature[])
+        throws
+        InstanceNotFoundException,
+        MBeanException,
+        ReflectionException {
+        try {
+            return connection().invoke(name,operationName,params,signature);
+        } catch (IOException x) {
+            throw wrapIOException(x,"invoke");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public boolean isInstanceOf(ObjectName name, String className)
+        throws InstanceNotFoundException {
+        try {
+            return connection().isInstanceOf(name, className);
+        } catch (IOException x) {
+            throw wrapIOException(x,"isInstanceOf");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public boolean isRegistered(ObjectName name) {
+        try {
+            return connection().isRegistered(name);
+        } catch (IOException x) {
+            throw wrapIOException(x,"isRegistered");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     * If an IOException is raised, returns an empty Set.
+     */
+    public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
+        try {
+            return connection().queryMBeans(name, query);
+        } catch (IOException x) {
+            throw wrapIOException(x,"queryMBeans");
+            //return Collections.emptySet();
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     * If an IOException is raised, returns an empty Set.
+     */
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        try {
+            return connection().queryNames(name, query);
+        } catch (IOException x) {
+            throw wrapIOException(x,"queryNames");
+            //return Collections.emptySet();
+        }
+    }
+
+    /**
+     * Throws an {@link UnsupportedOperationException}. This behavior can
+     * be changed by subclasses.
+     */
+    public ObjectInstance registerMBean(Object object, ObjectName name)
+        throws
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        NotCompliantMBeanException {
+        throw new UnsupportedOperationException("registerMBean");
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            connection().removeNotificationListener(name, listener);
+        } catch (IOException x) {
+            throw wrapIOException(x,"removeNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void removeNotificationListener(ObjectName name,
+                                           NotificationListener listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            connection().removeNotificationListener(name, listener,
+                                                    filter, handback);
+        } catch (IOException x) {
+            throw wrapIOException(x,"removeNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            connection().removeNotificationListener(name, listener);
+        } catch (IOException x) {
+            throw wrapIOException(x,"removeNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener,
+                                           NotificationFilter filter,
+                                           Object handback)
+        throws InstanceNotFoundException, ListenerNotFoundException {
+        try {
+            connection().removeNotificationListener(name, listener,
+                                                    filter, handback);
+        } catch (IOException x) {
+            throw wrapIOException(x,"removeNotificationListener");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void setAttribute(ObjectName name, Attribute attribute)
+        throws
+        InstanceNotFoundException,
+        AttributeNotFoundException,
+        InvalidAttributeValueException,
+        MBeanException,
+        ReflectionException {
+        try {
+            connection().setAttribute(name, attribute);
+        } catch (IOException x) {
+            throw wrapIOException(x,"setAttribute");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public AttributeList setAttributes(ObjectName name,
+                                       AttributeList attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        try {
+            return connection().setAttributes(name, attributes);
+        } catch (IOException x) {
+            throw wrapIOException(x,"setAttributes");
+        }
+    }
+
+    /**
+     * Forward this method to the
+     * wrapped object.
+     */
+    public void unregisterMBean(ObjectName name)
+        throws InstanceNotFoundException, MBeanRegistrationException {
+        try {
+            connection().unregisterMBean(name);
+        } catch (IOException x) {
+            throw wrapIOException(x,"unregisterMBean");
+        }
+    }
+
+    //----------------
+    // PRIVATE METHODS
+    //----------------
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/MBeanServerSupport.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,1339 @@
+/*
+ * 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 com.sun.jmx.defaults.JmxProperties;
+import com.sun.jmx.mbeanserver.Util;
+import java.io.ObjectInputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+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.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryEval;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * <p>Base class for custom implementations of the {@link MBeanServer}
+ * interface. The commonest use of this class is as the {@linkplain
+ * JMXNamespace#getSourceServer() source server} for a {@link
+ * JMXNamespace}, although this class can be used anywhere an {@code
+ * MBeanServer} instance is required. Note that the usual ways to
+ * obtain an {@code MBeanServer} instance are either to use {@link
+ * java.lang.management.ManagementFactory#getPlatformMBeanServer()
+ * ManagementFactory.getPlatformMBeanServer()} or to use the {@code
+ * newMBeanServer} or {@code createMBeanServer} methods from {@link
+ * javax.management.MBeanServerFactory MBeanServerFactory}. {@code
+ * MBeanServerSupport} is for certain cases where those are not
+ * appropriate.</p>
+ *
+ * <p>There are two main use cases for this class: <a
+ * href="#special-purpose">special-purpose MBeanServer implementations</a>,
+ * and <a href="#virtual">namespaces containing Virtual MBeans</a>. The next
+ * sections explain these use cases.</p>
+ *
+ * <p>In the simplest case, a subclass needs to implement only two methods:</p>
+ *
+ * <ul>
+ *     <li>
+ *         {@link #getNames getNames} which returns the name of
+ *         all MBeans handled by this {@code MBeanServer}.
+ *     </li>
+ *     <li>
+ *         {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a
+ *         {@link DynamicMBean} that can be used to invoke operations and
+ *         obtain meta data (MBeanInfo) on a given MBean.
+ *     </li>
+ * </ul>
+ *
+ * <p>Subclasses can create such {@link DynamicMBean} MBeans on the fly - for
+ * instance, using the class {@link javax.management.StandardMBean}, just for
+ * the duration of an MBeanServer method call.</p>
+ *
+ * <h4 id="special-purpose">Special-purpose MBeanServer implementations</h4>
+ *
+ * <p>In some cases
+ * the general-purpose {@code MBeanServer} that you get from
+ * {@link javax.management.MBeanServerFactory MBeanServerFactory} is not
+ * appropriate.  You might need different security checks, or you might
+ * want a mock {@code MBeanServer} suitable for use in tests, or you might
+ * want a simplified and optimized {@code MBeanServer} for a special purpose.</p>
+ *
+ * <p>As an example of a special-purpose {@code MBeanServer}, the class {@link
+ * javax.management.QueryNotificationFilter QueryNotificationFilter} constructs
+ * an {@code MBeanServer} instance every time it filters a notification,
+ * with just one MBean that represents the notification. Although it could
+ * use {@code MBeanServerFactory.newMBeanServer}, a special-purpose {@code
+ * MBeanServer} will be quicker to create, use less memory, and have simpler
+ * methods that execute faster.</p>
+ *
+ * <p>Here is an example of a special-purpose {@code MBeanServer}
+ * implementation that contains exactly one MBean, which is specified at the
+ * time of creation.</p>
+ *
+ * <pre>
+ * public class SingletonMBeanServer extends MBeanServerSupport {
+ *     private final ObjectName objectName;
+ *     private final DynamicMBean mbean;
+ *
+ *     public SingletonMBeanServer(ObjectName objectName, DynamicMBean mbean) {
+ *         this.objectName = objectName;
+ *         this.mbean = mbean;
+ *     }
+ *
+ *     &#64;Override
+ *     protected {@code Set<ObjectName>} {@link #getNames getNames}() {
+ *         return Collections.singleton(objectName);
+ *     }
+ *
+ *     &#64;Override
+ *     public DynamicMBean {@link #getDynamicMBeanFor
+ *                                getDynamicMBeanFor}(ObjectName name)
+ *             throws InstanceNotFoundException {
+ *         if (objectName.equals(name))
+ *             return mbean;
+ *         else
+ *             throw new InstanceNotFoundException(name);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>Using this class, you could make an {@code MBeanServer} that contains
+ * a {@link javax.management.timer.Timer Timer} MBean like this:</p>
+ *
+ * <pre>
+ *     Timer timer = new Timer();
+ *     DynamicMBean mbean = new {@link javax.management.StandardMBean
+ *                                     StandardMBean}(timer, TimerMBean.class);
+ *     ObjectName name = new ObjectName("com.example:type=Timer");
+ *     MBeanServer timerMBS = new SingletonMBeanServer(name, mbean);
+ * </pre>
+ *
+ * <p>When {@code getDynamicMBeanFor} always returns the same object for the
+ * same name, as here, notifications work in the expected way: if the object
+ * is a {@link NotificationEmitter} then listeners can be added using
+ * {@link MBeanServer#addNotificationListener(ObjectName, NotificationListener,
+ * NotificationFilter, Object) MBeanServer.addNotificationListener}.  If
+ * {@code getDynamicMBeanFor} does not always return the same object for the
+ * same name, more work is needed to make notifications work, as described
+ * <a href="#notifs">below</a>.</p>
+ *
+ * <h4 id="virtual">Namespaces containing Virtual MBeans</h4>
+ *
+ * <p>Virtual MBeans are MBeans that do not exist as Java objects,
+ * except transiently while they are being accessed.  This is useful when
+ * there might be very many of them, or when keeping track of their creation
+ * and deletion might be expensive or hard.  For example, you might have one
+ * MBean per system process.  With an ordinary {@code MBeanServer}, you would
+ * have to list the system processes in order to create an MBean object for
+ * each one, and you would have to track the arrival and departure of system
+ * processes in order to create or delete the corresponding MBeans.  With
+ * Virtual MBeans, you only need the MBean for a given process at the exact
+ * point where it is referenced with a call such as
+ * {@link MBeanServer#getAttribute MBeanServer.getAttribute}.</p>
+ *
+ * <p>Here is an example of an {@code MBeanServer} implementation that has
+ * one MBean for every system property.  The system property {@code "java.home"}
+ * is represented by the MBean called {@code
+ * com.example:type=Property,name="java.home"}, with an attribute called
+ * {@code Value} that is the value of the property.</p>
+ *
+ * <pre>
+ * public interface PropertyMBean {
+ *     public String getValue();
+ * }
+ *
+ * <a name="PropsMBS"></a>public class PropsMBS extends MBeanServerSupport {
+ *     private static ObjectName newObjectName(String name) {
+ *         try {
+ *             return new ObjectName(name);
+ *         } catch (MalformedObjectNameException e) {
+ *             throw new AssertionError(e);
+ *         }
+ *     }
+ *
+ *     public static class PropertyImpl implements PropertyMBean {
+ *         private final String name;
+ *
+ *         public PropertyImpl(String name) {
+ *             this.name = name;
+ *         }
+ *
+ *         public String getValue() {
+ *             return System.getProperty(name);
+ *         }
+ *     }
+ *
+ *     &#64;Override
+ *     public DynamicMBean {@link #getDynamicMBeanFor
+ *                                getDynamicMBeanFor}(ObjectName name)
+ *             throws InstanceNotFoundException {
+ *
+ *         // Check that the name is a legal one for a Property MBean
+ *         ObjectName namePattern = newObjectName(
+ *                     "com.example:type=Property,name=\"*\"");
+ *         if (!namePattern.apply(name))
+ *             throw new InstanceNotFoundException(name);
+ *
+ *         // Extract the name of the property that the MBean corresponds to
+ *         String propName = ObjectName.unquote(name.getKeyProperty("name"));
+ *         if (System.getProperty(propName) == null)
+ *             throw new InstanceNotFoundException(name);
+ *
+ *         // Construct and return a transient MBean object
+ *         PropertyMBean propMBean = new PropertyImpl(propName);
+ *         return new StandardMBean(propMBean, PropertyMBean.class, false);
+ *     }
+ *
+ *     &#64;Override
+ *     protected {@code Set<ObjectName>} {@link #getNames getNames}() {
+ *         {@code Set<ObjectName> names = new TreeSet<ObjectName>();}
+ *         Properties props = System.getProperties();
+ *         for (String propName : props.stringPropertyNames()) {
+ *             ObjectName objectName = newObjectName(
+ *                     "com.example:type=Property,name=" +
+ *                     ObjectName.quote(propName));
+ *             names.add(objectName);
+ *         }
+ *         return names;
+ *     }
+ * }
+ * </pre>
+ *
+ * <p id="virtual-notif-example">Because the {@code getDynamicMBeanFor} method
+ * returns a different object every time it is called, the default handling
+ * of notifications will not work, as explained <a href="#notifs">below</a>.
+ * In this case it does not matter, because the object returned by {@code
+ * getDynamicMBeanFor} is not a {@code NotificationEmitter}, so {@link
+ * MBeanServer#addNotificationListener(ObjectName, NotificationListener,
+ * NotificationFilter, Object) MBeanServer.addNotificationListener} will
+ * always fail. But if we wanted to extend {@code PropsMBS} so that the MBean
+ * for property {@code "foo"} emitted a notification every time that property
+ * changed, we would need to do it as shown below. (Because there is no API to
+ * be informed when a property changes, this code assumes that some other code
+ * calls the {@code propertyChanged} method every time a property changes.)</p>
+ *
+ * <pre>
+ * public class PropsMBS {
+ *     ...as <a href="#PropsMBS">above</a>...
+ *
+ *     private final {@link VirtualEventManager} vem = new VirtualEventManager();
+ *
+ *     &#64;Override
+ *     public NotificationEmitter {@link #getNotificationEmitterFor
+ *                                       getNotificationEmitterFor}(
+ *             ObjectName name) throws InstanceNotFoundException {
+ *         getDynamicMBeanFor(name);  // check that the name is valid
+ *         return vem.{@link VirtualEventManager#getNotificationEmitterFor
+ *                           getNotificationEmitterFor}(name);
+ *     }
+ *
+ *     public void propertyChanged(String name, String newValue) {
+ *         ObjectName objectName = newObjectName(
+ *                 "com.example:type=Property,name=" + ObjectName.quote(name));
+ *         Notification n = new Notification(
+ *                 "com.example.property.changed", objectName, 0L,
+ *                 "Property " + name + " changed");
+ *         n.setUserData(newValue);
+ *         vem.{@link VirtualEventManager#publish publish}(objectName, n);
+ *     }
+ * }
+ * </pre>
+ *
+ * <h4 id="creation">MBean creation and deletion</h4>
+ *
+ * <p>MBean creation through {@code MBeanServer.createMBean} is disabled
+ * by default. Subclasses which need to support MBean creation
+ * through {@code createMBean} need to implement a single method {@link
+ * #createMBean(String, ObjectName, ObjectName, Object[], String[],
+ * boolean)}.</p>
+ *
+ * <p>Similarly MBean registration and unregistration through {@code
+ * registerMBean} and {@code unregisterMBean} are disabled by default.
+ * Subclasses which need to support MBean registration and
+ * unregistration will need to implement {@link #registerMBean registerMBean}
+ * and {@link #unregisterMBean unregisterMBean}.</p>
+ *
+ * <h4 id="notifs">Notifications</h4>
+ *
+ * <p>By default {@link MBeanServer#addNotificationListener(ObjectName,
+ * NotificationListener, NotificationFilter, Object) addNotificationListener}
+ * is accepted for an MBean <em>{@code name}</em> if {@link #getDynamicMBeanFor
+ * getDynamicMBeanFor}<code>(<em>name</em>)</code> returns an object that is a
+ * {@link NotificationEmitter}.  That is appropriate if
+ * {@code getDynamicMBeanFor}<code>(<em>name</em>)</code> always returns the
+ * same object for the same <em>{@code name}</em>.  But with
+ * Virtual MBeans, every call to {@code getDynamicMBeanFor} returns a new object,
+ * which is discarded as soon as the MBean request has finished.
+ * So a listener added to that object would be immediately forgotten.</p>
+ *
+ * <p>The simplest way for a subclass that defines Virtual MBeans
+ * to support notifications is to create a private {@link VirtualEventManager}
+ * and override the method {@link
+ * #getNotificationEmitterFor getNotificationEmitterFor} as follows:</p>
+ *
+ * <pre>
+ *     private final VirtualEventManager vem = new VirtualEventManager();
+ *
+ *     &#64;Override
+ *     public NotificationEmitter getNotificationEmitterFor(
+ *             ObjectName name) throws InstanceNotFoundException {
+ *         // Check that the name is a valid Virtual MBean.
+ *         // This is the easiest way to do that, but not always the
+ *         // most efficient:
+ *         getDynamicMBeanFor(name);
+ *
+ *         // Return an object that supports add/removeNotificationListener
+ *         // through the VirtualEventManager.
+ *         return vem.getNotificationEmitterFor(name);
+ *     }
+ * </pre>
+ *
+ * <p>A notification <em>{@code n}</em> can then be sent from the Virtual MBean
+ * called <em>{@code name}</em> by calling {@link VirtualEventManager#publish
+ * vem.publish}<code>(<em>name</em>, <em>n</em>)</code>.  See the example
+ * <a href="#virtual-notif-example">above</a>.</p>
+ *
+ * @since 1.7
+ */
+public abstract class MBeanServerSupport implements MBeanServer {
+
+    /**
+     * A logger for this class.
+     */
+    private static final Logger LOG =
+            JmxProperties.NAMESPACE_LOGGER;
+
+    /**
+     * <p>Make a new {@code MBeanServerSupport} instance.</p>
+     */
+    protected MBeanServerSupport() {
+    }
+
+    /**
+     * <p>Returns a dynamically created handle that makes it possible to
+     * access the named MBean for the duration of a method call.</p>
+     *
+     * <p>An easy way to create such a {@link DynamicMBean} handle is, for
+     * instance, to create a temporary MXBean instance and to wrap it in
+     * an instance of
+     * {@link javax.management.StandardMBean}.
+     * This handle should remain valid for the duration of the call
+     * but can then be discarded.</p>
+     * @param name the name of the MBean for which a request was received.
+     * @return a {@link DynamicMBean} handle that can be used to invoke
+     * operations on the named MBean.
+     * @throws InstanceNotFoundException if no such MBean is supposed
+     *         to exist.
+     */
+    public abstract DynamicMBean getDynamicMBeanFor(ObjectName name)
+                        throws InstanceNotFoundException;
+
+    /**
+     * <p>Subclasses should implement this method to return
+     * the names of all MBeans handled by this object instance.</p>
+     *
+     * <p>The object returned by getNames() should be safely {@linkplain
+     * Set#iterator iterable} even in the presence of other threads that may
+     * cause the set of names to change. Typically this means one of the
+     * following:</p>
+     *
+     * <ul>
+     * <li>the returned set of names is always the same; or
+     * <li>the returned set of names is an object such as a {@link
+     * java.util.concurrent.CopyOnWriteArraySet CopyOnWriteArraySet} that is
+     * safely iterable even if the set is changed by other threads; or
+     * <li>a new Set is constructed every time this method is called.
+     * </ul>
+     *
+     * @return the names of all MBeans handled by this object.
+     */
+    protected abstract Set<ObjectName> getNames();
+
+    /**
+     * <p>List names matching the given pattern.
+     * The default implementation of this method calls {@link #getNames()}
+     * and returns the subset of those names matching {@code pattern}.</p>
+     *
+     * @param pattern an ObjectName pattern
+     * @return the list of MBean names that match the given pattern.
+     */
+    protected Set<ObjectName> getMatchingNames(ObjectName pattern) {
+        return Util.filterMatchingNames(pattern, getNames());
+    }
+
+    /**
+     * <p>Returns a {@link NotificationEmitter} which can be used to
+     * subscribe or unsubscribe for notifications with the named
+     * mbean.</p>
+     *
+     * <p>The default implementation of this method calls {@link
+     * #getDynamicMBeanFor getDynamicMBeanFor(name)} and returns that object
+     * if it is a {@code NotificationEmitter}, otherwise null. See <a
+     * href="#notifs">above</a> for further discussion of notification
+     * handling.</p>
+     *
+     * @param name The name of the MBean whose notifications are being
+     * subscribed, or unsuscribed.
+     *
+     * @return A {@link NotificationEmitter} that can be used to subscribe or
+     * unsubscribe for notifications emitted by the named MBean, or {@code
+     * null} if the MBean does not emit notifications and should not be
+     * considered as a {@code NotificationEmitter}.
+     *
+     * @throws InstanceNotFoundException if {@code name} is not the name of
+     * an MBean in this {@code MBeanServer}.
+     */
+    public NotificationEmitter getNotificationEmitterFor(ObjectName name)
+            throws InstanceNotFoundException {
+        DynamicMBean mbean = getDynamicMBeanFor(name);
+        if (mbean instanceof NotificationEmitter)
+            return (NotificationEmitter) mbean;
+        else
+            return null;
+    }
+
+    private NotificationEmitter getNonNullNotificationEmitterFor(
+            ObjectName name)
+            throws InstanceNotFoundException {
+        NotificationEmitter emitter = getNotificationEmitterFor(name);
+        if (emitter == null) {
+            IllegalArgumentException iae = new IllegalArgumentException(
+                    "Not a NotificationEmitter: " + name);
+            throw new RuntimeOperationsException(iae);
+        }
+        return emitter;
+    }
+
+    /**
+     * <p>Creates a new MBean in the MBean name space.
+     * This operation is not supported in this base class implementation.</p>
+     * The default implementation of this method always throws an {@link
+     * UnsupportedOperationException}
+     * wrapped in a {@link RuntimeOperationsException}.</p>
+     *
+     * <p>Subclasses may redefine this method to provide an implementation.
+     * All the various flavors of {@code MBeanServer.createMBean} methods
+     * will eventually call this method. A subclass that wishes to
+     * support MBean creation through {@code createMBean} thus only
+     * needs to provide an implementation for this one method.
+     *
+     * @param className The class name of the MBean to be instantiated.
+     * @param name The object name of the MBean. May be null.
+     * @param params An array containing the parameters of the
+     * constructor to be invoked.
+     * @param signature An array containing the signature of the
+     * constructor to be invoked.
+     * @param loaderName The object name of the class loader to be used.
+     * @param useCLR This parameter is {@code true} when this method
+     *        is called from one of the {@code MBeanServer.createMBean} methods
+     *        whose signature does not include the {@code ObjectName} of an
+     *        MBean class loader to use for loading the MBean class.
+     *
+     * @return An <CODE>ObjectInstance</CODE>, containing the
+     * <CODE>ObjectName</CODE> and the Java class name of the newly
+     * instantiated MBean.  If the contained <code>ObjectName</code>
+     * is <code>n</code>, the contained Java class name is
+     * <code>{@link javax.management.MBeanServer#getMBeanInfo
+     * getMBeanInfo(n)}.getClassName()</code>.
+     *
+     * @exception ReflectionException Wraps a
+     * <CODE>java.lang.ClassNotFoundException</CODE> or a
+     * <CODE>java.lang.Exception</CODE> that occurred when trying to
+     * invoke the MBean's constructor.
+     * @exception InstanceAlreadyExistsException The MBean is already
+     * under the control of the MBean server.
+     * @exception MBeanRegistrationException The
+     * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
+     * interface) method of the MBean has thrown an exception. The
+     * MBean will not be registered.
+     * @exception MBeanException The constructor of the MBean has
+     * thrown an exception
+     * @exception NotCompliantMBeanException This class is not a JMX
+     * compliant MBean
+     * @exception InstanceNotFoundException The specified class loader
+     * is not registered in the MBean server.
+     * @exception RuntimeOperationsException Wraps either:
+     * <ul>
+     * <li>a <CODE>java.lang.IllegalArgumentException</CODE>: The className
+     * passed in parameter is null, the <CODE>ObjectName</CODE> passed in
+     * parameter contains a pattern or no <CODE>ObjectName</CODE> is specified
+     * for the MBean; or</li>
+     * <li>an {@code UnsupportedOperationException} if creating MBeans is not
+     * supported by this {@code MBeanServer} implementation.
+     * </ul>
+     */
+    public ObjectInstance createMBean(String className,
+            ObjectName name, ObjectName loaderName, Object[] params,
+            String[] signature, boolean useCLR)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException {
+        throw newUnsupportedException("createMBean");
+    }
+
+
+    /**
+     * <p>Attempts to determine whether the named MBean should be
+     * considered as an instance of a given class.  The default implementation
+     * of this method calls {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
+     * to get an MBean object.  Then its behaviour is the same as the standard
+     * {@link MBeanServer#isInstanceOf MBeanServer.isInstanceOf} method.</p>
+     *
+     * {@inheritDoc}
+     */
+    public boolean isInstanceOf(ObjectName name, String className)
+        throws InstanceNotFoundException {
+
+        final DynamicMBean instance = nonNullMBeanFor(name);
+
+        try {
+            final String mbeanClassName = instance.getMBeanInfo().getClassName();
+
+            if (mbeanClassName.equals(className))
+                return true;
+
+            final Object resource;
+            final ClassLoader cl;
+            if (instance instanceof DynamicWrapperMBean) {
+                DynamicWrapperMBean d = (DynamicWrapperMBean) instance;
+                resource = d.getWrappedObject();
+                cl = d.getWrappedClassLoader();
+            } else {
+                resource = instance;
+                cl = instance.getClass().getClassLoader();
+            }
+
+            final Class<?> classNameClass = Class.forName(className, false, cl);
+
+            if (classNameClass.isInstance(resource))
+                return true;
+
+            if (classNameClass == NotificationBroadcaster.class ||
+                    classNameClass == NotificationEmitter.class) {
+                try {
+                    getNotificationEmitterFor(name);
+                    return true;
+                } catch (Exception x) {
+                    LOG.finest("MBean " + name +
+                            " is not a notification emitter. Ignoring: "+x);
+                    return false;
+                }
+            }
+
+            final Class<?> resourceClass = Class.forName(mbeanClassName, false, cl);
+            return classNameClass.isAssignableFrom(resourceClass);
+        } catch (Exception x) {
+            /* Could be SecurityException or ClassNotFoundException */
+            LOG.logp(Level.FINEST,
+                    MBeanServerSupport.class.getName(),
+                    "isInstanceOf", "Exception calling isInstanceOf", x);
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method returns the string
+     * "DefaultDomain".</p>
+     */
+    public String getDefaultDomain() {
+        return "DefaultDomain";
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method returns
+     * {@link #getNames()}.size().</p>
+     */
+    public Integer getMBeanCount() {
+        return getNames().size();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method first calls {@link #getNames
+     * getNames()} to get a list of all MBean names,
+     * and from this set of names, derives the set of domains which contain
+     * MBeans.</p>
+     */
+    public String[] getDomains() {
+        final Set<ObjectName> names = getNames();
+        final Set<String> res = new TreeSet<String>();
+        for (ObjectName n : names) {
+            if (n == null) continue; // not allowed but you never know.
+            res.add(n.getDomain());
+        }
+        return res.toArray(new String[res.size()]);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link
+     *    #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle
+     * to the named MBean,
+     * and then call {@link DynamicMBean#getAttribute getAttribute}
+     * on that {@link DynamicMBean} handle.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public Object getAttribute(ObjectName name, String attribute)
+        throws MBeanException, AttributeNotFoundException,
+               InstanceNotFoundException, ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        return mbean.getAttribute(attribute);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
+     * to obtain a handle to the named MBean,
+     * and then call {@link DynamicMBean#setAttribute setAttribute}
+     * on that {@link DynamicMBean} handle.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public void setAttribute(ObjectName name, Attribute attribute)
+        throws InstanceNotFoundException, AttributeNotFoundException,
+            InvalidAttributeValueException, MBeanException,
+            ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        mbean.setAttribute(attribute);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+     * handle to the named MBean,
+     * and then call {@link DynamicMBean#getAttributes getAttributes}
+     * on that {@link DynamicMBean} handle.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public AttributeList getAttributes(ObjectName name,
+            String[] attributes) throws InstanceNotFoundException,
+            ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        return mbean.getAttributes(attributes);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+     * handle to the named MBean,
+     * and then call {@link DynamicMBean#setAttributes setAttributes}
+     * on that {@link DynamicMBean} handle.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public AttributeList setAttributes(ObjectName name, AttributeList attributes)
+        throws InstanceNotFoundException, ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        return mbean.setAttributes(attributes);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+     * handle to the named MBean,
+     * and then call {@link DynamicMBean#invoke invoke}
+     * on that {@link DynamicMBean} handle.</p>
+     */
+    public Object invoke(ObjectName name, String operationName,
+                Object[] params, String[] signature)
+                throws InstanceNotFoundException, MBeanException,
+                       ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        return mbean.invoke(operationName, params, signature);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
+     * handle to the named MBean,
+     * and then call {@link DynamicMBean#getMBeanInfo getMBeanInfo}
+     * on that {@link DynamicMBean} handle.</p>
+     */
+    public MBeanInfo getMBeanInfo(ObjectName name)
+        throws InstanceNotFoundException, IntrospectionException,
+               ReflectionException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        return mbean.getMBeanInfo();
+   }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will call
+     * {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}.<!--
+     * -->{@link DynamicMBean#getMBeanInfo getMBeanInfo()}.<!--
+     * -->{@link MBeanInfo#getClassName getClassName()} to get the
+     * class name to combine with {@code name} to produce a new
+     * {@code ObjectInstance}.</p>
+     */
+    public ObjectInstance getObjectInstance(ObjectName name)
+            throws InstanceNotFoundException {
+        final DynamicMBean mbean = nonNullMBeanFor(name);
+        final String className = mbean.getMBeanInfo().getClassName();
+        return new ObjectInstance(name, className);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first call {@link
+     * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle to the
+     * named MBean. If {@code getDynamicMBeanFor} returns an object, {@code
+     * isRegistered} will return true. If {@code getDynamicMBeanFor} returns
+     * null or throws {@link InstanceNotFoundException}, {@code isRegistered}
+     * will return false.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public boolean isRegistered(ObjectName name) {
+        try {
+            final DynamicMBean mbean = getDynamicMBeanFor(name);
+            return mbean!=null;
+        } catch (InstanceNotFoundException x) {
+            if (LOG.isLoggable(Level.FINEST))
+                LOG.finest("MBean "+name+" is not registered: "+x);
+            return false;
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method will first
+     * call {@link #queryNames queryNames}
+     * to get a list of all matching MBeans, and then, for each returned name,
+     * call {@link #getObjectInstance getObjectInstance(name)}.</p>
+     */
+    public Set<ObjectInstance> queryMBeans(ObjectName pattern, QueryExp query) {
+        final Set<ObjectName> names = queryNames(pattern, query);
+        if (names.isEmpty()) return Collections.emptySet();
+        final Set<ObjectInstance> mbeans = new HashSet<ObjectInstance>();
+        for (ObjectName name : names) {
+            try {
+                mbeans.add(getObjectInstance(name));
+            } catch (SecurityException x) { // DLS: OK
+                continue;
+            } catch (InstanceNotFoundException x) { // DLS: OK
+                continue;
+            }
+        }
+        return mbeans;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method calls {@link #getMatchingNames
+     * getMatchingNames(pattern)} to obtain a list of MBeans matching
+     * the given name pattern. If the {@code query} parameter is null,
+     * this will be the result. Otherwise, it will evaluate the
+     * {@code query} parameter for each of the returned names, exactly
+     * as an {@code MBeanServer} would. This might result in
+     * {@link #getDynamicMBeanFor getDynamicMBeanFor} being called
+     * several times for each returned name.</p>
+     */
+    public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
+        try {
+            final Set<ObjectName> res = getMatchingNames(pattern);
+            return filterListOfObjectNames(res, query);
+        } catch (Exception x) {
+            LOG.fine("Unexpected exception raised in queryNames: "+x);
+            LOG.log(Level.FINEST, "Unexpected exception raised in queryNames", x);
+        }
+        // We reach here only when an exception was raised.
+        //
+        return Collections.emptySet();
+    }
+
+    private final static boolean apply(final QueryExp query,
+                  final ObjectName on,
+                  final MBeanServer srv) {
+        boolean res = false;
+        MBeanServer oldServer = QueryEval.getMBeanServer();
+        query.setMBeanServer(srv);
+        try {
+            res = query.apply(on);
+        } catch (Exception e) {
+            LOG.finest("QueryExp.apply threw exception, returning false." +
+                    " Cause: "+e);
+            res = false;
+        } finally {
+           /*
+            * query.setMBeanServer is probably
+            * QueryEval.setMBeanServer so put back the old
+            * value.  Since that method uses a ThreadLocal
+            * variable, this code is only needed for the
+            * unusual case where the user creates a custom
+            * QueryExp that calls a nested query on another
+            * MBeanServer.
+            */
+            query.setMBeanServer(oldServer);
+        }
+        return res;
+    }
+
+    /**
+     * Filters a {@code Set<ObjectName>} according to a pattern and a query.
+     * This might be quite inefficient for virtual name spaces.
+     */
+    Set<ObjectName>
+            filterListOfObjectNames(Set<ObjectName> list,
+                                    QueryExp query) {
+        if (list.isEmpty() || query == null)
+            return list;
+
+        // create a new result set
+        final Set<ObjectName> result = new HashSet<ObjectName>();
+
+        for (ObjectName on : list) {
+            // if on doesn't match query exclude it.
+            if (apply(query, on, this))
+                result.add(on);
+        }
+        return result;
+    }
+
+
+    // Don't use {@inheritDoc}, because we don't want to say that the
+    // MBeanServer replaces a reference to the MBean by its ObjectName.
+    /**
+     * <p>Adds a listener to a registered MBean. A notification emitted by
+     * the MBean will be forwarded to the listener.</p>
+     *
+     * <p>This implementation calls
+     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+     * and invokes {@code addNotificationListener} on the
+     * {@link NotificationEmitter} it returns.
+     *
+     * @see #getDynamicMBeanFor getDynamicMBeanFor
+     * @see #getNotificationEmitterFor getNotificationEmitterFor
+     */
+    public void addNotificationListener(ObjectName name,
+            NotificationListener listener, NotificationFilter filter,
+            Object handback) throws InstanceNotFoundException {
+        final NotificationEmitter emitter =
+                getNonNullNotificationEmitterFor(name);
+        emitter.addNotificationListener(listener, filter, handback);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation calls
+     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+     * and invokes {@code removeNotificationListener} on the
+     * {@link NotificationEmitter} it returns.
+     * @see #getDynamicMBeanFor getDynamicMBeanFor
+     * @see #getNotificationEmitterFor getNotificationEmitterFor
+     */
+    public void removeNotificationListener(ObjectName name,
+            NotificationListener listener)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        final NotificationEmitter emitter =
+                getNonNullNotificationEmitterFor(name);
+        emitter.removeNotificationListener(listener);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation calls
+     * {@link #getNotificationEmitterFor getNotificationEmitterFor}
+     * and invokes {@code removeNotificationListener} on the
+     * {@link NotificationEmitter} it returns.
+     * @see #getDynamicMBeanFor getDynamicMBeanFor
+     * @see #getNotificationEmitterFor getNotificationEmitterFor
+     */
+    public void removeNotificationListener(ObjectName name,
+            NotificationListener listener, NotificationFilter filter,
+            Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        NotificationEmitter emitter =
+                getNonNullNotificationEmitterFor(name);
+        emitter.removeNotificationListener(listener);
+    }
+
+
+    /**
+     * <p>Adds a listener to a registered MBean.</p>
+     *
+     * <p>The default implementation of this method first calls
+     * {@link #getDynamicMBeanFor getDynamicMBeanFor(listenerName)}.
+     * If that successfully returns an object, call it {@code
+     * mbean}, then (a) if {@code mbean} is an instance of {@link
+     * NotificationListener} then this method calls {@link
+     * #addNotificationListener(ObjectName, NotificationListener,
+     * NotificationFilter, Object) addNotificationListener(name, mbean, filter,
+     * handback)}, otherwise (b) this method throws an exception as specified
+     * for this case.</p>
+     *
+     * <p>This default implementation is not appropriate for Virtual MBeans,
+     * although that only matters if the object returned by {@code
+     * getDynamicMBeanFor} can be an instance of
+     * {@code NotificationListener}.</p>
+     *
+     * @throws RuntimeOperationsException {@inheritDoc}
+     */
+    public void addNotificationListener(ObjectName name, ObjectName listenerName,
+            NotificationFilter filter, Object handback)
+            throws InstanceNotFoundException {
+        NotificationListener listener = getListenerMBean(listenerName);
+        addNotificationListener(name, listener, filter, handback);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public void removeNotificationListener(ObjectName name,
+            ObjectName listenerName)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        NotificationListener listener = getListenerMBean(listenerName);
+        removeNotificationListener(name, listener);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public void removeNotificationListener(ObjectName name,
+            ObjectName listenerName, NotificationFilter filter,
+            Object handback)
+            throws InstanceNotFoundException, ListenerNotFoundException {
+        NotificationListener listener = getListenerMBean(listenerName);
+        removeNotificationListener(name, listener, filter, handback);
+    }
+
+    private NotificationListener getListenerMBean(ObjectName listenerName)
+            throws InstanceNotFoundException {
+        Object mbean = getDynamicMBeanFor(listenerName);
+        if (mbean instanceof NotificationListener)
+            return (NotificationListener) mbean;
+        else {
+            throw newIllegalArgumentException(
+                    "MBean is not a NotificationListener: " + listenerName);
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link InstanceNotFoundException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @return the default implementation of this method never returns.
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public ClassLoader getClassLoader(ObjectName loaderName)
+            throws InstanceNotFoundException {
+        final UnsupportedOperationException failed =
+                new UnsupportedOperationException("getClassLoader");
+        final InstanceNotFoundException x =
+                new InstanceNotFoundException(String.valueOf(loaderName));
+        x.initCause(failed);
+        throw x;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method calls
+     * {@link #getDynamicMBeanFor getDynamicMBeanFor(mbeanName)} and applies
+     * the logic just described to the result.</p>
+     */
+    public ClassLoader getClassLoaderFor(ObjectName mbeanName)
+            throws InstanceNotFoundException {
+        final DynamicMBean mbean = nonNullMBeanFor(mbeanName);
+        if (mbean instanceof DynamicWrapperMBean)
+            return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
+        else
+            return mbean.getClass().getClassLoader();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The default implementation of this method returns a
+     * {@link ClassLoaderRepository} containing exactly one loader,
+     * the {@linkplain Thread#getContextClassLoader() context class loader}
+     * for the current thread.
+     * Subclasses can override this method to return a different
+     * {@code ClassLoaderRepository}.</p>
+     */
+    public ClassLoaderRepository getClassLoaderRepository() {
+        // We return a new ClassLoaderRepository each time this
+        // method is called. This is by design, because the
+        // SingletonClassLoaderRepository is a very small object and
+        // getClassLoaderRepository() will not be called very often
+        // (the connector server calls it once) - in the context of
+        // MBeanServerSupport there's a very good chance that this method will
+        // *never* be called.
+        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+        return Util.getSingleClassLoaderRepository(ccl);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public ObjectInstance registerMBean(Object object, ObjectName name)
+            throws InstanceAlreadyExistsException, MBeanRegistrationException,
+            NotCompliantMBeanException {
+        throw newUnsupportedException("registerMBean");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public void unregisterMBean(ObjectName name)
+            throws InstanceNotFoundException, MBeanRegistrationException {
+        throw newUnsupportedException("unregisterMBean");
+    }
+
+    /**
+     * Calls {@link #createMBean(String, ObjectName,
+     *           ObjectName, Object[], String[], boolean)
+     * createMBean(className, name, null, params, signature, true)};
+     */
+    public final ObjectInstance createMBean(String className, ObjectName name,
+            Object[] params, String[] signature)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException {
+        try {
+            return safeCreateMBean(className, name, null, params, signature, true);
+        } catch (InstanceNotFoundException ex) {
+            // should not happen!
+            throw new MBeanException(ex, "Unexpected exception: " + ex);
+        }
+    }
+
+    /**
+     * Calls {@link #createMBean(String, ObjectName,
+     *           ObjectName, Object[], String[], boolean)
+     * createMBean(className,name, loaderName, params, signature, false)};
+     */
+    public final ObjectInstance createMBean(String className, ObjectName name,
+            ObjectName loaderName, Object[] params, String[] signature)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException {
+        return safeCreateMBean(className, name, loaderName, params, signature, false);
+    }
+
+    /**
+     * Calls {@link #createMBean(String, ObjectName,
+     *           ObjectName, Object[], String[], boolean)
+     * createMBean(className, name, null, null, null, true)};
+     */
+    public final ObjectInstance createMBean(String className, ObjectName name)
+        throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException {
+        try {
+            return safeCreateMBean(className, name, null, null, null, true);
+        } catch (InstanceNotFoundException ex) {
+            // should not happen!
+            throw new MBeanException(ex, "Unexpected exception: " + ex);
+        }
+    }
+
+    /**
+     * Calls {@link #createMBean(String, ObjectName,
+     *           ObjectName, Object[], String[], boolean)
+     * createMBean(className, name, loaderName, null, null, false)};
+     */
+    public final ObjectInstance createMBean(String className, ObjectName name,
+            ObjectName loaderName)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException {
+        return safeCreateMBean(className, name, loaderName, null, null, false);
+    }
+
+    // make sure all exceptions are correctly wrapped in a JMXException
+    private ObjectInstance safeCreateMBean(String className,
+            ObjectName name, ObjectName loaderName, Object[] params,
+            String[] signature, boolean useRepository)
+            throws ReflectionException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException,
+            NotCompliantMBeanException, InstanceNotFoundException {
+        try {
+            return createMBean(className, name, loaderName, params,
+                               signature, useRepository);
+        } catch (ReflectionException x) { throw x;
+        } catch (InstanceAlreadyExistsException x) { throw x;
+        } catch (MBeanRegistrationException x) { throw x;
+        } catch (MBeanException x) { throw x;
+        } catch (NotCompliantMBeanException x) { throw x;
+        } catch (InstanceNotFoundException x) { throw x;
+        } catch (SecurityException x) { throw x;
+        } catch (JMRuntimeException x) { throw x;
+        } catch (RuntimeException x) {
+            throw new RuntimeOperationsException(x, x.toString());
+        } catch (Exception x) {
+            throw new MBeanException(x, x.toString());
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public Object instantiate(String className)
+            throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public Object instantiate(String className, ObjectName loaderName)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public Object instantiate(String className, Object[] params,
+            String[] signature) throws ReflectionException, MBeanException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    public Object instantiate(String className, ObjectName loaderName,
+            Object[] params, String[] signature)
+            throws ReflectionException, MBeanException,
+            InstanceNotFoundException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(ObjectName name, byte[] data)
+            throws InstanceNotFoundException, OperationsException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className, byte[] data)
+            throws OperationsException, ReflectionException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This operation is not supported in this base class implementation.
+     * The default implementation of this method always throws
+     * {@link RuntimeOperationsException} wrapping
+     * {@link UnsupportedOperationException}.</p>
+     *
+     * @throws javax.management.RuntimeOperationsException wrapping
+     *        {@link UnsupportedOperationException}
+     */
+    @Deprecated
+    public ObjectInputStream deserialize(String className,
+            ObjectName loaderName, byte[] data)
+            throws InstanceNotFoundException, OperationsException,
+            ReflectionException {
+        throw new UnsupportedOperationException("Not applicable.");
+    }
+
+
+    // Calls getDynamicMBeanFor, and throws an InstanceNotFoundException
+    // if the returned mbean is null.
+    // The DynamicMBean returned by this method is thus guaranteed to be
+    // non null.
+    //
+    private DynamicMBean nonNullMBeanFor(ObjectName name)
+            throws InstanceNotFoundException {
+        if (name == null)
+            throw newIllegalArgumentException("Null ObjectName");
+        if (name.getDomain().equals("")) {
+            String defaultDomain = getDefaultDomain();
+            try {
+                name = name.withDomain(getDefaultDomain());
+            } catch (Exception e) {
+                throw newIllegalArgumentException(
+                        "Illegal default domain: " + defaultDomain);
+            }
+        }
+        final DynamicMBean mbean = getDynamicMBeanFor(name);
+        if (mbean!=null) return mbean;
+        throw new InstanceNotFoundException(String.valueOf(name));
+    }
+
+    static RuntimeException newUnsupportedException(String operation) {
+        return new RuntimeOperationsException(
+            new UnsupportedOperationException(
+                operation+": Not supported in this namespace"));
+    }
+
+    static RuntimeException newIllegalArgumentException(String msg) {
+        return new RuntimeOperationsException(
+                new IllegalArgumentException(msg));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/VirtualEventManager.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,378 @@
+/*
+ * 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 com.sun.jmx.remote.util.ClassLogger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.management.InstanceNotFoundException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.event.EventConsumer;
+
+/**
+ * <p>This class maintains a list of subscribers for ObjectName patterns and
+ * allows a notification to be sent to all subscribers for a given ObjectName.
+ * It is typically used in conjunction with {@link MBeanServerSupport}
+ * to implement a namespace with Virtual MBeans that can emit notifications.
+ * The {@code VirtualEventManager} keeps track of the listeners that have been
+ * added to each Virtual MBean. When an event occurs that should trigger a
+ * notification from a Virtual MBean, the {@link #publish publish} method can
+ * be used to send it to the appropriate listeners.</p>
+ * @since 1.7
+ */
+public class VirtualEventManager implements EventConsumer {
+    /**
+     * <p>Create a new {@code VirtualEventManager}.</p>
+     */
+    public VirtualEventManager() {
+    }
+
+    public void subscribe(
+            ObjectName name,
+            NotificationListener listener,
+            NotificationFilter filter,
+            Object handback) {
+
+        if (logger.traceOn())
+            logger.trace("subscribe", "" + name);
+
+        if (name == null)
+            throw new IllegalArgumentException("Null MBean name");
+
+        if (listener == null)
+            throw new IllegalArgumentException("Null listener");
+
+        Map<ObjectName, List<ListenerInfo>> map =
+                name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
+
+        final ListenerInfo li = new ListenerInfo(listener, filter, handback);
+        List<ListenerInfo> list;
+
+        synchronized (map) {
+            list = map.get(name);
+            if (list == null) {
+                list = new ArrayList<ListenerInfo>();
+                map.put(name, list);
+            }
+            list.add(li);
+        }
+    }
+
+    public void unsubscribe(
+            ObjectName name, NotificationListener listener)
+            throws ListenerNotFoundException {
+
+        if (logger.traceOn())
+            logger.trace("unsubscribe2", "" + name);
+
+        if (name == null)
+            throw new IllegalArgumentException("Null MBean name");
+
+        if (listener == null)
+            throw new ListenerNotFoundException();
+
+        Map<ObjectName, List<ListenerInfo>> map =
+                name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
+
+        final ListenerInfo li = new ListenerInfo(listener, null, null);
+        List<ListenerInfo> list;
+        synchronized (map) {
+            list = map.get(name);
+            if (list == null || !list.remove(li))
+                throw new ListenerNotFoundException();
+
+            if (list.isEmpty())
+                map.remove(name);
+        }
+    }
+
+    /**
+     * <p>Unsubscribes a listener which is listening to an MBean or a set of
+     * MBeans represented by an {@code ObjectName} pattern.</p>
+     *
+     * <p>The listener to be removed must have been added by the {@link
+     * #subscribe subscribe} method with the given {@code name}, {@code filter},
+     * and {@code handback}. If the {@code
+     * name} is a pattern, then the {@code subscribe} must have used the same
+     * pattern. If the same listener has been subscribed more than once to the
+     * {@code name} with the same filter and handback, only one listener is
+     * removed.</p>
+     *
+     * @param name The name of the MBean or an {@code ObjectName} pattern
+     * representing a set of MBeans to which the listener was subscribed.
+     * @param listener A listener that was previously subscribed to the
+     * MBean(s).
+     *
+     * @throws ListenerNotFoundException The given {@code listener} was not
+     * subscribed to the given {@code name}.
+     *
+     * @see #subscribe
+     */
+    public void unsubscribe(
+            ObjectName name, NotificationListener listener,
+            NotificationFilter filter, Object handback)
+            throws ListenerNotFoundException {
+
+        if (logger.traceOn())
+            logger.trace("unsubscribe4", "" + name);
+
+        if (name == null)
+            throw new IllegalArgumentException("Null MBean name");
+
+        if (listener == null)
+            throw new ListenerNotFoundException();
+
+        Map<ObjectName, List<ListenerInfo>> map =
+                name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
+
+        List<ListenerInfo> list;
+        synchronized (map) {
+            list = map.get(name);
+            boolean removed = false;
+            for (Iterator<ListenerInfo> it = list.iterator(); it.hasNext(); ) {
+                ListenerInfo li = it.next();
+                if (li.equals(listener, filter, handback)) {
+                    it.remove();
+                    removed = true;
+                    break;
+                }
+            }
+            if (!removed)
+                throw new ListenerNotFoundException();
+
+            if (list.isEmpty())
+                map.remove(name);
+        }
+    }
+
+    /**
+     * <p>Sends a notification to the subscribers for a given MBean.</p>
+     *
+     * <p>For each listener subscribed with an {@code ObjectName} that either
+     * is equal to {@code emitterName} or is a pattern that matches {@code
+     * emitterName}, if the associated filter accepts the notification then it
+     * is forwarded to the listener.</p>
+     *
+     * @param emitterName The name of the MBean emitting the notification.
+     * @param n The notification being sent by the MBean called
+     * {@code emitterName}.
+     *
+     * @throws IllegalArgumentException If the emitterName of the
+     * notification is null or is an {@code ObjectName} pattern.
+     */
+    public void publish(ObjectName emitterName, Notification n) {
+        if (logger.traceOn())
+            logger.trace("publish", "" + emitterName);
+
+        if (n == null)
+            throw new IllegalArgumentException("Null notification");
+
+        if (emitterName == null) {
+            throw new IllegalArgumentException(
+                    "Null emitter name");
+        } else if (emitterName.isPattern()) {
+            throw new IllegalArgumentException(
+                    "The emitter must not be an ObjectName pattern");
+        }
+
+        final List<ListenerInfo> listeners = new ArrayList<ListenerInfo>();
+
+        // If there are listeners for this exact name, add them.
+        synchronized (exactSubscriptionMap) {
+            List<ListenerInfo> exactListeners =
+                    exactSubscriptionMap.get(emitterName);
+            if (exactListeners != null)
+                listeners.addAll(exactListeners);
+        }
+
+        // Loop over subscription patterns, and add all listeners for each
+        // one that matches the emitterName name.
+        synchronized (patternSubscriptionMap) {
+            for (ObjectName on : patternSubscriptionMap.keySet()) {
+                if (on.apply(emitterName))
+                    listeners.addAll(patternSubscriptionMap.get(on));
+            }
+        }
+
+        // Send the notification to all the listeners we found.
+        sendNotif(listeners, n);
+    }
+
+    /**
+     * <p>Returns a {@link NotificationEmitter} object which can be used to
+     * subscribe or unsubscribe for notifications with the named
+     * mbean.  The returned object implements {@link
+     * NotificationEmitter#addNotificationListener
+     * addNotificationListener(listener, filter, handback)} as
+     * {@link #subscribe this.subscribe(name, listener, filter, handback)}
+     * and the two {@code removeNotificationListener} methods from {@link
+     * NotificationEmitter} as the corresponding {@code unsubscribe} methods
+     * from this class.</p>
+     *
+     * @param name   The name of the MBean whose notifications are being
+     *        subscribed, or unsuscribed.
+     *
+     * @return A {@link NotificationEmitter}
+     *         that can be used to subscribe or unsubscribe for
+     *         notifications emitted by the named MBean, or {@code null} if
+     *         the MBean does not emit notifications and should not
+     *         be considered as a {@code NotificationBroadcaster}.  This class
+     *         never returns null but a subclass is allowed to.
+     *
+     * @throws InstanceNotFoundException if {@code name} does not exist.
+     * This implementation never throws {@code InstanceNotFoundException} but
+     * a subclass is allowed to override this method to do so.
+     */
+    public NotificationEmitter
+            getNotificationEmitterFor(final ObjectName name)
+            throws InstanceNotFoundException {
+        final NotificationEmitter emitter = new NotificationEmitter() {
+            public void addNotificationListener(NotificationListener listener,
+                    NotificationFilter filter, Object handback)
+                    throws IllegalArgumentException {
+                subscribe(name, listener, filter, handback);
+            }
+
+            public void removeNotificationListener(
+                    NotificationListener listener)
+                    throws ListenerNotFoundException {
+                unsubscribe(name, listener);
+            }
+
+            public void removeNotificationListener(NotificationListener listener,
+                                                   NotificationFilter filter,
+                                                   Object handback)
+                    throws ListenerNotFoundException {
+                unsubscribe(name, listener, filter, handback);
+            }
+
+            public MBeanNotificationInfo[] getNotificationInfo() {
+                // Never called.
+                return null;
+            }
+        };
+        return emitter;
+    }
+
+    // ---------------------------------
+    // private stuff
+    // ---------------------------------
+
+    private static class ListenerInfo {
+        public final NotificationListener listener;
+        public final NotificationFilter filter;
+        public final Object handback;
+
+        public ListenerInfo(NotificationListener listener,
+                NotificationFilter filter,
+                Object handback) {
+
+            if (listener == null) {
+                throw new IllegalArgumentException("Null listener.");
+            }
+
+            this.listener = listener;
+            this.filter = filter;
+            this.handback = handback;
+        }
+
+        /* Two ListenerInfo instances are equal if they have the same
+         * NotificationListener.  This means that we can use List.remove
+         * to implement the two-argument removeNotificationListener.
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof ListenerInfo)) {
+                return false;
+            }
+
+            return listener.equals(((ListenerInfo)o).listener);
+        }
+
+        /* Method that compares all four fields, appropriate for the
+         * four-argument removeNotificationListener.
+         */
+        boolean equals(
+                NotificationListener listener,
+                NotificationFilter filter,
+                Object handback) {
+            return (this.listener == listener && same(this.filter, filter)
+                    && same(this.handback, handback));
+        }
+
+        private static boolean same(Object x, Object y) {
+            if (x == y)
+                return true;
+            if (x == null)
+                return false;
+            return x.equals(y);
+        }
+
+        @Override
+        public int hashCode() {
+            return listener.hashCode();
+        }
+    }
+
+    private static void sendNotif(List<ListenerInfo> listeners, Notification n) {
+        for (ListenerInfo li : listeners) {
+            if (li.filter == null ||
+                    li.filter.isNotificationEnabled(n)) {
+                try {
+                    li.listener.handleNotification(n, li.handback);
+                } catch (Exception e) {
+                    logger.trace("sendNotif", "handleNotification", e);
+                }
+            }
+        }
+    }
+
+    // ---------------------------------
+    // private variables
+    // ---------------------------------
+
+    private final Map<ObjectName, List<ListenerInfo>> exactSubscriptionMap =
+            new HashMap<ObjectName, List<ListenerInfo>>();
+    private final Map<ObjectName, List<ListenerInfo>> patternSubscriptionMap =
+            new HashMap<ObjectName, List<ListenerInfo>>();
+
+    // trace issue
+    private static final ClassLogger logger =
+            new ClassLogger("javax.management.event", "EventManager");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/javax/management/namespace/package-info.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,597 @@
+/*
+ * 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.
+ */
+
+/**
+ *  <p>The <code>javax.management.namespace</code> package makes it possible
+ *  to federate MBeanServers into a hierarchical name space.</p>
+ *
+ *  <h3 id="WhatIs">What Is a Name Space?</h3>
+ *  <p>
+ *      A name space is like an {@link javax.management.MBeanServer} within
+ *      an {@code MBeanServer}. Just as a file system folder can contain
+ *      another file system folder, an {@code MBeanServer} can contain another
+ *      {@code MBeanServer}. Similarly, just as a remote folder on a remote
+ *      disk can be mounted on a parent folder on a local disk, a remote name
+ *      space in a remote {@code MBeanServer} can be mounted on a name
+ *      space in a local parent {@code MBeanServer}.
+ *  </p>
+ *  <p>
+ *      The <code>javax.management.namespace</code> API thus makes it possible to
+ *      create a hierarchy of MBean servers federated in a hierarchical name
+ *      space inside a single {@code MBeanServer}.
+ *  </p>
+ *  <h3 id="HowToCreate">How To Create a Name Space?</h3>
+ *  <p>
+ *      To create a name space, you only need to register a
+ *      {@link javax.management.namespace.JMXNamespace} MBean in
+ *      an MBean server. We have seen that a namespace is like
+ *      an {@code MBeanServer} within an {@code MBeanServer}, and
+ *      therefore, it is possible to create a namespace that shows the
+ *      content of another {@code MBeanServer}. The simplest case is
+ *      when that {@code MBeanServer} is another {@code MBeanServer}
+ *      created by the {@link javax.management.MBeanServerFactory} as
+ *      shown in the extract below:
+ *  </p>
+ *  <pre>
+ *  final MBeanServer server = ....;
+ *  final String namespace = "foo";
+ *  final ObjectName namespaceName = {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName
+ *        JMXNamespaces.getNamespaceObjectName(namespace)};
+ *  server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
+ *                      namespaceName);
+ *  </pre>
+ *  <p id="NamespaceView">
+ *     To navigate in namespaces and view their content, the easiest way is
+ *     to use an instance of {@link javax.management.namespace.JMXNamespaceView}. For instance, given
+ *     the {@code server} above, in which we created a namespace {@code "foo"},
+ *     it is possible to create a {@code JMXNamespaceView} that will make it
+ *     possible to navigate easily in the namespaces and sub-namespaces of that
+ *     server:
+ *  </p>
+ *  <pre>
+ *  // create a namespace view for 'server'
+ *  final JMXNamespaceView view = new JMXNamespaceView(server);
+ *
+ *  // list all top level namespaces in 'server'
+ *  System.out.println("List of namespaces: " + Arrays.toString({@link javax.management.namespace.JMXNamespaceView#list() view.list()}));
+ *
+ *  // go down into namespace 'foo': provides a namespace view of 'foo' and its
+ *  // sub namespaces...
+ *  final JMXNamespaceView foo = {@link javax.management.namespace.JMXNamespaceView#down view.down("foo")};
+ *
+ *  // list all MBeans contained in namespace 'foo'
+ *  System.out.println({@link javax.management.namespace.JMXNamespaceView#where() foo.where()} + " contains: " +
+ *         {@link javax.management.namespace.JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
+ *  </pre>
+ * <p>
+ *   It is also possible to create more complex namespaces, such as namespaces
+ *   that point to MBean servers located in remote JVMs.
+ * </p>
+ * <p>
+ *      For instance, to mount the MBeanServer accessible
+ *      at <code>service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi</code>
+ *      in a name space {@code "foo"} inside the {@linkplain
+ *      java.lang.management.ManagementFactory#getPlatformMBeanServer platform
+ *      MBeanServer} you would write the following piece of code:
+ *  </p>
+ *  <pre>
+ *      final JMXServiceURL sourceURL =
+ *         new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi");
+ *      final MBeanServer platform = ManagementFactory.getPlatformMBeanServer();
+ *      final Map&lt;String,Object&gt; options = Collections.emptyMap();
+ *      final JMXRemoteNamespace mbean = {@link
+ *            javax.management.namespace.JMXRemoteNamespace JMXRemoteNamespace}.
+ *         {@link javax.management.namespace.JMXRemoteNamespace#newJMXRemoteNamespace newJMXRemoteNamespace(sourceURL, options)};
+ *      final ObjectName name = {@link javax.management.namespace.JMXNamespaces JMXNamespaces}.{@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName(String) getNamespaceObjectName("foo")};
+ *      final ObjectInstance ref = platform.registerMBean(mbean,name);
+ *      platform.invoke(ref.getObjectName(),"connect",null,null);
+ *  </pre>
+ *
+ *  <h3 id="WhatLike">What Does a Name Space Look Like?</h3>
+ *
+ *  <p>
+ *   We have seen that {@link javax.management.namespace.JMXNamespaceView} class
+ *   provides an easy way to navigate within namespaces. It is however also
+ *   possible to interact with namespaces directly from the top level
+ *   {@code MBeanServer} in which they have been created.
+ *      From the outside, a name space only appears as a special MBean in
+ *      the MBean server. There's nothing much you can do with this MBean
+ *      directly.
+ *  </p>
+ *  <p>
+ *      For instance, let's assume you have registered a {@link
+ *      javax.management.namespace.JMXRemoteNamespaceMBean
+ *      JMXRemoteNamespaceMBean} to manage the name space {@code "foo"}.
+ *      <br>If you query for
+ *      <code>platform.queryNames("&#42;//:*",null)</code>, then you will see
+ *      one MBean named {@code "foo//:type=JMXNamespace"}.
+ *      <br>This is the {@link javax.management.namespace.JMXNamespace}
+ *      MBean which is in charge of handling the namespace {@code "foo"}.
+ *  </p>
+ *  <p>
+ *      In fact, name space handler MBeans are instances of
+ *      the class {@link javax.management.namespace.JMXNamespace} - or
+ *      instances of a subclass of that class.
+ *      They have a special {@link javax.management.ObjectName} defined by
+ *      {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName
+ *      JMXNamespaces.getNamespaceObjectName}.<br>
+ *      {@link javax.management.namespace.JMXNamespace} instances are able
+ *      to return an {@link
+ *      javax.management.namespace.JMXNamespace#getSourceServer MBeanServer}
+ *      which corresponds to the MBeanServer within (= the name space itself).
+ *  </p>
+ *  <p>
+ *      So how does it work? How can you see the MBeans contained in the new
+ *      name space?
+ *  </p>
+ *  <p>In order to address scalability issues, MBeans registered in
+ *     namespaces (such as our namespace {@code "foo"} above) can not be
+ *     seen with {@code mbeanServer.queryNames("*:*",null)}. To see the MBeans
+ *     contained in a namespace, you can use one of these methods:
+ *  </p>
+ *  <ol>
+ *      <li>
+ *          You can use the {@link javax.management.namespace.JMXNamespaceView}
+ *          class <a href="#NamespaceView">shown above</a>,
+ *      </li>
+ *      <li>
+ *          or you can <a href="#NamespacePrefix">directly look</a> for MBeans
+ *          whose names match
+ *         {@code "foo//*:*"},
+ *      </li>
+ *      <li>
+ *          or you can <a href="#ChangeTo">narrow down</a> to the namespace
+ *          and obtain an MBeanServer
+ *          proxy that corresponds to an MBeanServer view of that namespace.
+ *          The JMXNamespaces class provides a static method that
+ *          allows you to narrow down to a name space, by calling
+ *          {@link javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String)
+ *                 JMXNamespaces.narrowToNamespace}.
+ *      </li>
+ *  </ol>
+ *
+ *  <h3 id="NamespacePrefix">Using Name Space Prefixes</h3>
+ *  <p>
+ *      As we have explained above, MBeans contained in name
+ *      spaces are not returned by {@code server.queryNames(null,null)} - or
+ *      <code>server.queryNames({@link javax.management.ObjectName#WILDCARD ObjectName.WILDCARD},null)</code>.
+ *      <br>
+ *      However, these MBeans can still be accessed from the top level
+ *      {@code MBeanServer} interface, without using any API specific to the
+ *      version 2.0 of the JMX API, simply by using object names with
+ *      name space prefixes:
+ *      <br>To list MBeans contained in a namespace {@code "foo"} you can
+ *      query for MBeans whose names match {@code "foo//*:*"}, as shown
+ *      earlier in this document:
+ *      <pre>
+ *         server.queryNames(new ObjectName("foo//*:*", null);
+ *         // or equivalently:
+ *         server.queryNames(JMXNamespaces.getWildcardFor("foo"), null);
+ *      </pre>
+ *      This will return a list of MBean names whose domain name starts
+ *      with {@code foo//}.
+ *  </p><p>
+ *      Using these names, you can invoke any operation on the corresponding
+ *      MBeans. For instance, to get the {@link javax.management.MBeanInfo
+ *      MBeanInfo} of an MBean
+ *      contained in name space {@code "foo"} (assuming
+ *      the name of the MBean within its name space is <i>domain:type=Thing</i>,
+ *      then simply call:
+ *      <pre>
+ *         server.getMBeanInfo(new ObjectName("foo//domain:type=Thing"));
+ *      </pre>
+ *      An easier way to access MBeans contained in a name space is to
+ *      <i>cd</i> inside the name space, as shown in the following paragraph.
+ *  </p>
+ *
+ *  <h3 id="ChangeTo">Narrowing Down Into a Name Spaces</h3>
+ *  <p>
+ *      As we have seen, name spaces are like MBean servers within MBean servers.
+ *      Therefore, it is possible to view a name space just as if it were
+ *      an other MBean server. This is similar to opening a sub
+ *      folder from a parent folder.<br>
+ *      This operation is illustrated in the code extract below:
+ *      <pre>
+ *          final MBeanServer foo =
+ *                JMXNamespaces.narrowToNamespace(platform, "foo");
+ *          final MBeanInfo info =
+ *                foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
+ *      </pre>
+ *      The {@code MBeanServer} returned by {@link
+ *      javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String)
+ *      JMXNamespaces.narrowToNamespace} is an {@code MBeanServer} view that
+ *      narrows down into a given namespace. The MBeans contained inside that
+ *      namespace can now be accessed by their regular local name. <br>
+ *      The MBean server obtained by narrowing down
+ *      to name space {@code "foo"} behaves just like a regular MBean server.
+ *      However, it may sometimes throw an {@link
+ *      java.lang.UnsupportedOperationException UnsupportedOperationException}
+ *      wrapped in a JMX exception if you try to call an operation which is not
+ *      supported by the underlying name space handler.
+ *      <br>For instance, {@link javax.management.MBeanServer#registerMBean
+ *      registerMBean} is not supported for name spaces mounted from remote
+ *      MBean servers.
+ *  </p>
+ *  <p>
+ *      <u>Note:</u> If you have a deep hierarchy of namespaces, and if you
+ *      are switching from one namespace to another in the course of your
+ *      application, it might be more convenient to use a
+ *      {@link javax.management.namespace.JMXNamespaceView}
+ *      in order to navigate in your namespaces.
+ *  </p>
+ *
+ *  <h3 id="NamespaceTypes">Different Types of Name Spaces</h3>
+ *      <p>
+ *          This API lets you create several types of name spaces:
+ *          <ul>
+ *              <li id="RemoteNS">
+ *                  You can use the {@link
+ *                  javax.management.namespace.JMXRemoteNamespace
+ *                  JMXRemoteNamespace} to create
+ *                  <b>remote</b> name spaces, mounted from
+ *                  a remote sub {@code MBeanServer} source, as shown
+ *                  <a href="#HowToCreate">earlier</a> in this document.
+ *              </li>
+ *              <li id="LocalNS">
+ *                  You can also use {@link
+ *                  javax.management.namespace.JMXNamespace
+ *                  JMXNamespace} to create
+ *                  <b>local</b> name spaces,
+ *                  by providing a direct reference to another {@code MBeanServer}
+ *                  instance living in the same JVM.
+ *              </li>
+ *              <li id="VirtualNS">
+ *                  Finally, you can create
+ *                  name spaces containing <b>virtual</b> MBeans,
+ *                  by subclassing the {@link
+ *                  javax.management.namespace.MBeanServerSupport
+ *                  MBeanServerSupport}, and passing an instance of
+ *                  your own subclass to a {@link
+ *                  javax.management.namespace.JMXNamespace JMXNamespace}.
+ *              </li>
+ *              <li id="CustomNS">
+ *                  If none of these classes suit your needs, you can also provide
+ *                  <b>your own</b> subclass of {@link
+ *                  javax.management.namespace.JMXNamespace
+ *                  JMXNamespace}. This is however discouraged.
+ *              </li>
+ *          </ul>
+ *      </p>
+ *
+ *      <h3 id="SpecialOp">Name Spaces And Special Operations</h3>
+ *      <p>
+ *          MBean Naming considerations aside, Name Spaces are transparent for
+ *          most {@code MBeanServer} operations. There are however a few
+ *          exceptions:
+ *      </p>
+ *      <ul>
+ *          <li>
+ *              <p>MBeanServer only operations - these are the operations which are
+ *              supported by {@link javax.management.MBeanServer MBeanServer} but
+ *              are not present in {@link
+ *              javax.management.MBeanServerConnection
+ *              MBeanServerConnection}. Since a name space can be a local view of
+ *              a remote {@code MBeanServer}, accessible only through an
+ *              {@code MBeanServerConnection}, these
+ *              kinds of operations are not always supported.</p>
+ *              <ul>
+ *                  <li id="registerMBean">
+ *                      <p>registerMBean:</p>
+ *                      <p> The {@link javax.management.MBeanServer#registerMBean
+ *                          registerMBean}
+ *                          operation is not supported by most name spaces. A call
+ *                          to
+ *                          <pre>
+ *   MBeanServer server = ....;
+ *   ThingMBean mbean = new Thing(...);
+ *   ObjectName name = new ObjectName("foo//domain:type=Thing");
+ *   server.registerMBean(mbean, name);
+ *                          </pre>
+ *                          will usually fail, unless the name space
+ *                          {@code "foo"} is a <a href="#LocalNS">local</a> name
+ *                          space. In the case where you attempt to cross
+ *                          multiple name spaces, then all name spaces in the
+ *                          path must support the {@code registerMBean} operation
+ *                          in order for it to succeed.<br>
+ *                          To create an MBean inside a name space, it is
+ *                          usually safer to use {@code createMBean} -
+ *                          although some <a href="#MBeanCreation">special
+ *                          considerations</a> can also apply.
+ *                      </p>
+ *         <p></p>
+ *                  </li>
+ *                  <li id="getClassLoader">
+ *                      <p>getClassLoader:</p>
+ *                      <p> Similarly to <a href="#registerMBean">registerMBean</a>,
+ *                          and for the same reasons, {@link
+ *                          javax.management.MBeanServer#getClassLoader
+ *                          getClassLoader} will usually fail, unless the
+ *                          class loader is an MBean registered in a
+ *                          <a href="#LocalNS">local</a> name space.<br>
+ *                      </p>
+ *                  </li>
+ *                  <li id="getClassLoaderFor">
+ *                      <p>getClassLoaderFor:</p>
+ *                      <p> The implementation of {@link
+ *                          javax.management.MBeanServer#getClassLoaderFor
+ *                          getClassLoaderFor} also depends on which
+ *                          <a href="#NamespaceTypes">type of name space</a>
+ *                          handler is used across the namespace path.
+ *                      </p>
+ *                      <p>
+ *                          A <a href="#LocalNS">local</a> name space will usually
+ *                          be able to implement this method just as a real
+ *                          {@code MBeanServer} would. A
+ *                          <a href="#RemoteNS">remote</a> name space will usually
+ *                          return the default class loader configured on the
+ *                          internal {@link javax.management.remote.JMXConnector
+ *                          JMXConnector} used to connect to the remote server.
+ *                          When a {@link
+ *                          javax.management.namespace.JMXRemoteNamespace
+ *                          JMXRemoteNamespace} is used to connect to a
+ *                          remote server that contains MBeans which export
+ *                          custom types, the {@link
+ *                          javax.management.namespace.JMXRemoteNamespace
+ *                          JMXRemoteNamespace} must thus be configured with
+ *                          an options map such that the underlying connector
+ *                          can obtain a default class loader able
+ *                          to handle those types.
+ *                      </p>
+ *                      <p>
+ *                          Other <a href="#NamespaceTypes">types of name spaces</a>
+ *                          may implement this method
+ *                          as best as they can.
+ *                      </p>
+ *                  </li>
+ *              </ul>
+ *          </li>
+ *          <li id="MBeanCreation">
+ *              <p>MBean creation</p>
+ *              <p> MBean creation through {@link
+ *                  javax.management.MBeanServerConnection#createMBean
+ *                  createMBean} might not be supported by all
+ *                  name spaces: <a href="#LocalNS">local</a> name spaces and
+ *                  <a href="#LocalNS">remote</a> name spaces will usually
+ *                  support it, but <a href="#VirtualNS">virtual</a> name
+ *                  spaces and <a href="#CustomNS">custom</a> name
+ *                  spaces might not.
+ *              </p>
+ *              <p>
+ *                  In that case, they will throw an {@link
+ *                  java.lang.UnsupportedOperationException
+ *                  UnsupportedOperationException} usually wrapped into an {@link
+ *                  javax.management.MBeanRegistrationException}.
+ *              </p>
+ *          </li>
+ *          <li id="Notifications">
+ *              <p>Notifications</p>
+ *              <p> Some namespaces might not support JMX Notifications. In that
+ *                  case, a call to add or remove notification listener for an
+ *                  MBean contained in that name space will raise a
+ *                  {@link javax.management.RuntimeOperationsException
+ *                  RuntimeOperationsException} wrapping an {@link
+ *                  java.lang.UnsupportedOperationException
+ *                  UnsupportedOperationException} exception.
+ *              </p>
+ *          </li>
+ *      </ul>
+ *
+ *      <h3 id="CrossingNamespace">Crossing Several Name Spaces</h3>
+ *      <p>
+ *          Just as folders can contain other folders, name spaces can contain
+ *          other name spaces. For instance, if an {@code MBeanServer} <i>S1</i>
+ *          containing a name space {@code "bar"} is mounted in another
+ *          {@code MBeanServer} <i>S2</i> with name space {@code "foo"}, then
+ *          an MBean <i>M1</i> named {@code "domain:type=Thing"} in namespace
+ *          {@code "bar"} will appear as {@code "foo//bar//domain:type=Thing"} in
+ *          {@code MBeanServer} <i>S2</i>.
+ *      </p>
+ *      <p>
+ *          When accessing the MBean <i>M1</i> from server <i>S2</i>, the
+ *          method call will traverse in a cascade {@code MBeanServer} <i>S2</i>,
+ *          then the name space handler for name space {@code "foo"}, then
+ *          {@code MBeanServer} <i>S1</i>, before coming to the name space
+ *          handler for name space {@code "bar"}.  Any operation invoked
+ *          on the MBean from a "top-level" name space will therefore need to
+ *          traverse all the name spaces along the name space path until
+ *          it eventually reaches the named MBean. This means that an operation
+ *          like <a href="#registerMBean">registerMBean</a> for instance,
+ *          can only succeed if all the name spaces along the path support it.
+ *      </p>
+ *      <p>
+ *          Narrowing to a nested name space works just the same as narrowing
+ *          to a top level name space:
+ *      <pre>
+ *          final MBeanServer S2 = .... ;
+ *          final MBeanServer bar =
+ *                JMXNamespaces.narrowToNamespace(S2, "foo//bar");
+ *          final MBeanInfo info =
+ *                foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
+ *      </pre>
+ *      </p>
+ *
+ *      <h3 id="OperationResult">Name Spaces And Operation Results</h3>
+ *      <p>
+ *          Operation results, as well as attribute values returned by an MBean
+ *          contained in a name space must be interpreted in the context of that
+ *          name space.<br>
+ *          In other words, if an MBean in name space "foo" has an attribute of
+ *          type {@code ObjectName}, then it must be assumed that the
+ *          {@code ObjectName} returned by that MBean is relative to
+ *          name space "foo".<br>
+ *          The same rule aplies for MBean names that can be returned by
+ *          operations invoked on such an MBean. If one of the MBean operations
+ *          return, say, a {@code Set<ObjectName>} then those MBean names must
+ *          also be assumed to be relative to name space "foo".<br>
+ *      </p>
+ *      <p>
+ *          In the usual case, a JMX client will first
+ *          <a href="#ChangeTo">narrow to a name space</a> before invoking
+ *          any operation on the MBeans it contains. In that case the names
+ *          returned by the MBean invoked can be directly fed back to the
+ *          narrowed connection.
+ *          <br>
+ *          If however, the JMX client directly invoked the MBean from a higher
+ *          name space, without having narrowed to that name space first, then
+ *          the names that might be returned by that MBean will not be directly
+ *          usable - the JMX client will need to either
+ *          <a href="#ChangeTo">narrow to the name space</a> before using the
+ *          returned names, or convert the names to the higher level name space
+ *          context.
+ *          <br>
+ *          The {@link javax.management.namespace.JMXNamespaces JMXNamespaces}
+ *          class provides methods that can be used to perform that conversion.
+ *      </p>
+ *
+ *      <h3 id="NamespacesAndNotifications">Name Spaces And Notifications</h3>
+ *      <p>
+ *          As <a href="#WhatIs">already explained</a>, name spaces are very
+ *          similar to {@code MBeanServer}s. It is thus possible to get
+ *          {@link javax.management.MBeanServerNotification MBeanServerNotifications}
+ *          when MBeans are added or removed within a name space, by registering
+ *          with the {@link javax.management.MBeanServerDelegate
+ *          MBeanServerDelegate} MBean of the corresponding name space.<br>
+ *          However, it must be noted that the notifications emitted by a
+ *          name space must be interpreted in the context of that name space.
+ *          For instance, if an MBean {@code "domain:type=Thing"} contained in
+ *          namespace "foo//bar" emits a notification, the source of the
+ *          notification will be {@code "domain:type=Thing"}, not
+ *          {@code "foo//bar//domain:type=Thing"}. <br>
+ *          It is therefore recommended to keep track of the name space
+ *          information when registering a listener with an MBean contained in
+ *          a name space, especially if the same listener is used to receive
+ *          notifications from different name spaces. An easy solution is to
+ *          use the handback, as illustrated in the code below.
+ *          <pre>
+ *            final MBeanServer server = ...;
+ *            final NotificationListener listener = new NotificationListener() {
+ *                public void handleNotification(Notification n, Object handback) {
+ *                    if (!(n instanceof MBeanServerNotification)) {
+ *                        System.err.println("Error: expected MBeanServerNotification");
+ *                        return;
+ *                    }
+ *                    final MBeanServerNotification mbsn =
+ *                            (MBeanServerNotification) n;
+ *
+ *                    // We will pass the namespace path in the handback.
+ *                    //
+ *                    // The received notification must be interpreted in
+ *                    // the context of its source - therefore
+ *                    // mbsn.getMBeanName() does not include the name space
+ *                    // path...
+ *                    //
+ *                    final String namespace = (String) handback;
+ *                    System.out.println("Received " + mbsn.getType() +
+ *                            " for MBean " + mbsn.getMBeanName() +
+ *                            " from name space " + namespace);
+ *                }
+ *            };
+ *            server.addNotificationListener(JMXNamespaces.insertPath("foo//bar",
+ *                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//bar");
+ *            server.addNotificationListener(JMXNamespaces.insertPath("foo//joe",
+ *                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//joe");
+ *          </pre>
+ *      </p>
+ *      <p>
+ *          JMX Connectors may require some configuration in order to be able
+ *          to forward notifications from MBeans located in name spaces.
+ *          The RMI JMX Connector Server
+ *          in the Java SE 7 platform is configured by default to internally
+ *          use the new {@linkplain javax.management.event event service} on
+ *          the server side.
+ *          When the connector server is configured in this way, JMX clients
+ *          which use the old JMX Notifications mechanism (such as clients
+ *          running on prior versions of the JDK) will be able to
+ *          to receive notifications from MBeans located in sub name spaces.
+ *          This is because the connector server will transparently delegate
+ *          their subscriptions to the underlying {@linkplain
+ *          javax.management.event event service}. In summary:
+ *          <ul>
+ *              <li>
+ *                  On the server side: When exporting an {@code MBeanServer}
+ *                  through a JMX Connector, you will need to make sure that the
+ *                  connector server uses the new {@linkplain javax.management.event
+ *                  event service} in order to register for notifications. If the
+ *                  connector server doesn't use the event service, only clients
+ *                  which explicitly use the new {@linkplain javax.management.event
+ *                  event service} will be able to register for notifications
+ *                  with MBeans located in sub name spaces.
+ *              </li>
+ *              <li>
+ *                  On the client side: if the JMX Connector server (on the remote
+ *                  server side) was configured to internally use the new
+ *                  {@linkplain javax.management.event
+ *                  event service}, then clients can continue to use the old
+ *                  {@code MBeanServerConnection} add / remove notification
+ *                  listener methods transparently. Otherwise, only clients which
+ *                  explicitly use the new {@linkplain javax.management.event
+ *                  event service} will be able to receive notifications from
+ *                  MBeans contained in sub name spaces.
+ *              </li>
+ *          </ul>
+ *      </p>
+ *      <p>
+ *          These configuration issues apply at each node in the name space path,
+ *          whenever the name space points to a remote server. The
+ *          {@link javax.management.namespace.JMXRemoteNamespace
+ *          JMXRemoteNamespace} can be configured in such a way that it will
+ *          explicitly use an {@link javax.management.event.EventClient EventClient}
+ *          when forwarding subscription to the remote side. Note that this can be
+ *          unnecessary (and a waste of resources) if the underlying JMXConnector
+ *          returned by the JMXConnectorFactory (client side) already uses the
+ *          {@linkplain javax.management.event event service} to register for
+ *          notifications with the server side.
+ *      </p>
+ *
+ *      <h3 id="Security">Name Spaces And Access Control</h3>
+ *      <p>
+ *          Access to MBeans exposed through JMX namespaces is controlled by
+ *          {@linkplain javax.management.namespace.JMXNamespacePermission
+ *           jmx namespace permissions}. These permissions are checked by the
+ *          MBeanServer in which the {@link
+ *            javax.management.namespace.JMXNamespace JMXNamespace} MBean is registered.
+ *          This is <a href="JMXNamespace.html#PermissionChecks">described in
+ *          details</a> in the {@link
+ *            javax.management.namespace.JMXNamespace JMXNamespace} class.
+ *      </p>
+ *      <p>
+ *         To implement a "firewall-like" access control in a JMX agent you
+ *         can also place an {@link
+ *         javax.management.remote.MBeanServerForwarder} in the JMX Connector
+ *         Server which exposes the top-level MBeanServer of your application.
+ *         This {@code MBeanServerForwarder} will be able to perform
+ *         authorization checks for all MBeans, including those located in
+ *         sub name spaces.
+ *      </p>
+ *      <p>
+ *         For a tighter access control we recommend using a {@link
+ *         java.lang.SecurityManager security manager}.
+ *      </p>
+ * @since 1.7
+ * <p></p>
+ **/
+
+package javax.management.namespace;
+
--- a/src/share/classes/javax/management/remote/JMXConnectorFactory.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnectorFactory.java	Thu Sep 04 14:46:36 2008 +0200
@@ -268,6 +268,14 @@
         return conn;
     }
 
+    private static <K,V> Map<K,V> newHashMap() {
+        return new HashMap<K,V>();
+    }
+
+    private static <K> Map<K,Object> newHashMap(Map<K,?> map) {
+        return new HashMap<K,Object>(map);
+    }
+
     /**
      * <p>Creates a connector client for the connector server at the
      * given address.  The resultant client is not connected until its
@@ -300,16 +308,18 @@
     public static JMXConnector newJMXConnector(JMXServiceURL serviceURL,
                                                Map<String,?> environment)
             throws IOException {
-        Map<String, Object> envcopy;
+
+        final Map<String,Object> envcopy;
         if (environment == null)
-            envcopy = new HashMap<String, Object>();
+            envcopy = newHashMap();
         else {
             EnvHelp.checkAttributes(environment);
-            envcopy = new HashMap<String, Object>(environment);
+            envcopy = newHashMap(environment);
         }
 
         final ClassLoader loader = resolveClassLoader(envcopy);
-        final Class<JMXConnectorProvider> targetInterface = JMXConnectorProvider.class;
+        final Class<JMXConnectorProvider> targetInterface =
+                JMXConnectorProvider.class;
         final String protocol = serviceURL.getProtocol();
         final String providerClassName = "ClientProvider";
 
@@ -351,9 +361,10 @@
             }
         }
 
-        envcopy = Collections.unmodifiableMap(envcopy);
+        final Map<String,Object> fixedenv =
+                Collections.unmodifiableMap(envcopy);
 
-        return provider.newJMXConnector(serviceURL, envcopy);
+        return provider.newJMXConnector(serviceURL, fixedenv);
     }
 
     private static String resolvePkgs(Map env) throws JMXProviderException {
@@ -365,8 +376,8 @@
 
         if (pkgsObject == null)
             pkgsObject =
-                AccessController.doPrivileged(new PrivilegedAction<Object>() {
-                    public Object run() {
+                AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
                         return System.getProperty(PROTOCOL_PROVIDER_PACKAGES);
                     }
                 });
@@ -423,8 +434,7 @@
     static <T> Iterator<T> getProviderIterator(final Class<T> providerClass,
                                                final ClassLoader loader) {
        ServiceLoader<T> serviceLoader =
-                ServiceLoader.load(providerClass,
-                loader);
+                ServiceLoader.load(providerClass, loader);
        return serviceLoader.iterator();
     }
 
@@ -528,8 +538,8 @@
         }
 
         if (loader == null)
-            loader =
-                AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+            loader = AccessController.doPrivileged(
+                    new PrivilegedAction<ClassLoader>() {
                         public ClassLoader run() {
                             return
                                 Thread.currentThread().getContextClassLoader();
--- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java	Thu Sep 04 14:46:36 2008 +0200
@@ -77,6 +77,7 @@
 import javax.management.event.EventClientDelegateMBean;
 import javax.management.event.EventClientNotFoundException;
 import javax.management.event.FetchingEventForwarder;
+import javax.management.namespace.JMXNamespaces;
 import javax.management.remote.JMXServerErrorException;
 import javax.management.remote.NotificationResult;
 import javax.management.remote.TargetedNotification;
@@ -1292,11 +1293,27 @@
         public void removeNotificationListener(ObjectName name, Integer id)
             throws InstanceNotFoundException, ListenerNotFoundException,
                 IOException {
+            if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
+                logger.debug("removeNotificationListener",
+                        "This connector server is not configured to support " +
+                        "forwarding of notification subscriptions to name spaces");
+                throw new RuntimeOperationsException(
+                    new UnsupportedOperationException(
+                    "removeNotificationListener on name space MBeans. "));
+                }
             forwarder.removeNotificationListener(name,id);
         }
 
         public void removeNotificationListener(ObjectName name, Integer[] ids)
             throws Exception {
+            if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
+                logger.debug("removeNotificationListener",
+                        "This connector server is not configured to support " +
+                        "forwarding of notification subscriptions to name spaces");
+                throw new RuntimeOperationsException(
+                    new UnsupportedOperationException(
+                    "removeNotificationListener on name space MBeans. "));
+            }
             forwarder.removeNotificationListener(name,ids);
         }
 
@@ -1307,6 +1324,14 @@
         public Integer addNotificationListener(ObjectName name,
                 NotificationFilter filter)
             throws InstanceNotFoundException, IOException {
+            if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
+                logger.debug("addNotificationListener",
+                        "This connector server is not configured to support " +
+                        "forwarding of notification subscriptions to name spaces");
+                throw new RuntimeOperationsException(
+                    new UnsupportedOperationException(
+                    "addNotificationListener on name space MBeans. "));
+            }
             return forwarder.addNotificationListener(name,filter);
         }
 
@@ -1326,6 +1351,7 @@
         private final boolean checkNotificationEmission;
         private final String clientId;
         private final String connectionId;
+        private volatile String mbeanServerName;
 
         EventSubscriptionManager(
                 MBeanServer mbeanServer,
@@ -1343,6 +1369,11 @@
             this.connectionId = connectionId;
         }
 
+        private String mbeanServerName() {
+            if (mbeanServerName != null) return mbeanServerName;
+            else return (mbeanServerName = getMBeanServerName(mbeanServer));
+        }
+
         @SuppressWarnings("serial")  // no serialVersionUID
         private class AccessControlFilter implements NotificationFilter {
             private final NotificationFilter wrapped;
@@ -1357,7 +1388,8 @@
                 try {
                     if (checkNotificationEmission) {
                         ServerNotifForwarder.checkMBeanPermission(
-                                mbeanServer, name, "addNotificationListener");
+                                mbeanServerName(), mbeanServer, name,
+                                "addNotificationListener");
                     }
                     notifAC.fetchNotification(
                             connectionId, name, notification, getSubject());
@@ -1392,7 +1424,7 @@
             if (notifAC != null)
                 notifAC.removeNotificationListener(connectionId, name, getSubject());
             try {
-                delegate.removeListenerOrSubscriber(clientId,id);
+                delegate.removeListenerOrSubscriber(clientId, id);
             } catch (EventClientNotFoundException x) {
                 throw new IOException("Unknown clientId: "+clientId,x);
             }
@@ -1405,7 +1437,7 @@
                 notifAC.removeNotificationListener(connectionId, name, getSubject());
             try {
                 for (Integer id : ids)
-                    delegate.removeListenerOrSubscriber(clientId,id);
+                    delegate.removeListenerOrSubscriber(clientId, id);
             } catch (EventClientNotFoundException x) {
                 throw new IOException("Unknown clientId: "+clientId,x);
             }
@@ -1867,6 +1899,15 @@
         return e;
     }
 
+    private static String getMBeanServerName(final MBeanServer server) {
+        final PrivilegedAction<String> action = new PrivilegedAction<String>() {
+            public String run() {
+                return Util.getMBeanServerSecurityName(server);
+            }
+        };
+        return AccessController.doPrivileged(action);
+    }
+
     private static final Object[] NO_OBJECTS = new Object[0];
     private static final String[] NO_STRINGS = new String[0];
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2003 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test named MBeanServers.
+ * @author Daniel Fuchs
+ * @run clean NamedMBeanServerTest
+ * @run build NamedMBeanServerTest
+ * @run main NamedMBeanServerTest
+ */
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerBuilder;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerFactory;
+
+/**
+ * This test can probably be leveraged in the JCK to test compatibilty
+ * of MBeanServerFactory *Name* method implementation.
+ * @author dfuchs
+ */
+public class NamedMBeanServerTest {
+
+    /**
+     * One enum value for each way of creating an MBeanServer through the
+     * MBeanServerFactory
+     */
+    public static enum Creator {
+        newMBeanServer() {
+            public MBeanServer create(String domain) {
+                return MBeanServerFactory.newMBeanServer(domain);
+            }
+            public String test(MBeanServer server, String domain) {
+                System.out.println(toString()+"("+domain+")");
+                return test(server,
+                        MBeanServerFactory.DEFAULT_MBEANSERVER_NAME,
+                        domain);
+            }
+            public MBeanServer[] servers(Config config) {
+                return config.ndServers;
+            }
+            public String[] strings(Config config) {
+                return domains(config);
+            }
+            public String[] domains(Config config) {
+                return config.newDomains;
+            }
+            public String[] names(Config config) {
+                return null;
+            }
+        },
+        createMBeanServer() {
+            public MBeanServer create(String domain) {
+                return MBeanServerFactory.createMBeanServer(domain);
+            }
+            public String test(MBeanServer server, String domain) {
+                System.out.println(toString()+"("+domain+")");
+                return test(server,MBeanServerFactory.DEFAULT_MBEANSERVER_NAME,
+                        domain);
+            }
+            public MBeanServer[] servers(Config config) {
+                return config.cdServers;
+            }
+            public String[] strings(Config config) {
+                return domains(config);
+            }
+            public String[] domains(Config config) {
+                return config.createDomains;
+            }
+            public String[] names(Config config) {
+                return null;
+            }
+        },
+        newNamedMBeanServer() {
+            public MBeanServer create(String name) {
+                return MBeanServerFactory.newNamedMBeanServer(name,null);
+            }
+            public String test(MBeanServer server, String name) {
+                System.out.println(toString()+"("+name+",null)");
+                return test(server,name,"DefaultDomain");
+            }
+            public MBeanServer[] servers(Config config) {
+                return config.nnServers;
+            }
+            public String[] strings(Config config) {
+                return names(config);
+            }
+            public String[] domains(Config config) {
+                return null;
+            }
+            public String[] names(Config config) {
+                return config.newNames;
+            }
+        },
+        createNamedMBeanServer() {
+            public MBeanServer create(String name) {
+                return MBeanServerFactory.createNamedMBeanServer(name,null);
+            }
+            public String test(MBeanServer server, String name) {
+                System.out.println(toString()+"("+name+",null)");
+                return test(server,name,"DefaultDomain");
+            }
+            public MBeanServer[] servers(Config config) {
+                return config.cnServers;
+            }
+            public String[] strings(Config config) {
+                return names(config);
+            }
+            public String[] domains(Config config) {
+                return null;
+            }
+            public String[] names(Config config) {
+                return config.createNames;
+            }
+        };
+
+        // creates an MBeanServer using the specified input string.
+        // either a domain, (for UNNAMED) or a mbeanServerName (for NAMED)
+        public abstract MBeanServer create(String string);
+
+        // test the created server against the string used as input to create
+        // it.
+        public abstract String test(MBeanServer server, String ref);
+
+        public abstract MBeanServer[] servers(Config config);
+        public abstract String[] strings(Config config);
+        public abstract String[] names(Config config);
+        public abstract String[] domains(Config config);
+
+        public MBeanServer[] servers(Config config, String... refs) {
+            final MBeanServer[] servers = servers(config);
+            final String[] strings = strings(config);
+            final MBeanServer[] res = new MBeanServer[refs.length];
+            for (int i=0;i<refs.length;i++) {
+                for (int j=0;j<strings.length;j++) {
+                    if (strings[j].equals(refs[i]))
+                        res[i]=servers[j];
+                }
+                if (res[i] == null)
+                    throw new IllegalArgumentException(refs[i]);
+            }
+            return res;
+        }
+
+        String test(MBeanServer server, String name, String domain) {
+            // whether the MBeanServer was created throug a "create" method
+            boolean registered = REFERENCED.contains(this);
+            if (!server.getDefaultDomain().equals(domain)) {
+                return "Unexpected default domain: " +
+                        server.getDefaultDomain() + ", should be: " + domain;
+            }
+            if (!MBeanServerFactory.getMBeanServerName(server).
+                    equals(name)) {
+                return " Unexpected name: " +
+                        MBeanServerFactory.getMBeanServerName(server) +
+                        ", should be: " + name;
+            }
+            List<MBeanServer> found =
+                    MBeanServerFactory.findMBeanServerByName(name);
+            if (!registered && found.contains(server))
+                return " Server "+name+" found by name - " +
+                        "but should not be registered";
+            if (!registered &&
+                    !name.equals(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME) &&
+                    found.size()>0)
+                return " Server "+name+" had too many matches: " + found.size();
+            if (registered && !found.contains(server))
+                return " Server "+name+" not found by name - " +
+                        "but is registered!";
+            if (registered &&
+                    !name.equals(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME) &&
+                    !(found.size()==1))
+                return " Server "+name+" had too many matches: " + found.size();
+            return null;
+        }
+
+        public static final EnumSet<Creator> NAMED =
+                EnumSet.of(createNamedMBeanServer, newNamedMBeanServer);
+        public static final EnumSet<Creator> UNNAMED =
+                EnumSet.complementOf(NAMED);
+        public static final EnumSet<Creator> REFERENCED =
+                EnumSet.of(createMBeanServer, createNamedMBeanServer);
+        public static final EnumSet<Creator> UNREFERENCED =
+                EnumSet.complementOf(REFERENCED);
+
+    }
+
+    public static class Config {
+        final String[] newDomains;
+        final String[] createDomains;
+        final String[] newNames;
+        final String[] createNames;
+        final MBeanServer[] ndServers;
+        final MBeanServer[] cdServers;
+        final MBeanServer[] nnServers;
+        final MBeanServer[] cnServers;
+        final Map<String,Set<MBeanServer>> queries;
+        Config(String[][] data) {
+            this(data[0],data[1],data[2],data[3]);
+        }
+        Config(String[] nd, String[] cd, String[] nn, String[] cn) {
+            this.newDomains=nd.clone();
+            this.createDomains=cd.clone();
+            this.newNames=nn.clone();
+            this.createNames=cn.clone();
+            ndServers = new MBeanServer[nd.length];
+            cdServers = new MBeanServer[cd.length];
+            nnServers = new MBeanServer[nn.length];
+            cnServers = new MBeanServer[cn.length];
+            queries = new HashMap<String,Set<MBeanServer>>();
+            init();
+        }
+        private void init() {
+            for (Creator c : Creator.values()) fill(c);
+            addQuery(null,Creator.createMBeanServer.servers(this));
+            addQuery(null,Creator.createNamedMBeanServer.servers(this));
+            addQuery("?*",Creator.createMBeanServer.servers(this));
+            addQuery("?*",Creator.createNamedMBeanServer.servers(this));
+            addQuery("*",Creator.createMBeanServer.servers(this));
+            addQuery("*",Creator.createNamedMBeanServer.servers(this));
+            addQuery(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME,
+                    Creator.createMBeanServer.servers(this));
+        }
+        private void addQuery(String pattern, MBeanServer... servers) {
+            final Set<MBeanServer> s = getQuery(pattern);
+            s.addAll(Arrays.asList(servers));
+        }
+        public Set<MBeanServer> getQuery(String pattern) {
+            final Set<MBeanServer> s = queries.get(pattern);
+            if (s != null) return s;
+            queries.put(pattern,new HashSet<MBeanServer>());
+            return queries.get(pattern);
+        }
+        public Set<String> getPatterns() {
+            return queries.keySet();
+        }
+        private void fill(Creator creator) {
+            fill(creator.servers(this),creator.strings(this),creator);
+        }
+        private void fill(MBeanServer[] dest, String[] src, Creator creator) {
+            for(int i=0;i<src.length;i++) dest[i]=creator.create(src[i]);
+        }
+
+    }
+
+    static String[] domains(String... str) {
+        return str;
+    }
+    static String[] names(String... str) {
+        return str;
+    }
+    final static Config test1  = new Config(domains("foo1","foo2","foo3"),
+            domains("foobar1","foobar2","foobar3","foobar4"),
+            names("bar1","bar2"),
+            names("barfoo1","barfoo2","barfoo3","batfox1","catfog2","foofoo3"));
+    static {
+        test1.addQuery("b*",Creator.createNamedMBeanServer.servers(test1,
+                "barfoo1","barfoo2","barfoo3","batfox1"));
+        test1.addQuery("*arf*",Creator.createNamedMBeanServer.servers(test1,
+                "barfoo1","barfoo2","barfoo3"));
+        test1.addQuery("*a?f*",Creator.createNamedMBeanServer.servers(test1,
+                "barfoo1","barfoo2","barfoo3","batfox1","catfog2"));
+        test1.addQuery("",new MBeanServer[0]);
+        test1.addQuery("-",new MBeanServer[0]);
+        test1.addQuery("def*",Creator.createMBeanServer.servers(test1));
+    }
+
+    public static void test(Config config) throws Exception {
+        for (Creator c : Creator.values()) {
+            final MBeanServer[] s = c.servers(config);
+            final String[] ref = c.strings(config);
+            for (int i=0;i<s.length;i++) {
+                final String msg = c.test(s[i], ref[i]);
+                if (msg != null)
+                    throw new Exception(String.valueOf(c)+"["+i+"]: "+msg);
+            }
+        }
+        for (String pat : config.getPatterns()) {
+            System.out.print("findMBeanServerByName(\""+pat+"\"): [");
+            final List<MBeanServer> found =
+                    MBeanServerFactory.findMBeanServerByName(pat);
+            String sep=" ";
+            for (MBeanServer m : found) {
+                System.out.print(sep+MBeanServerFactory.getMBeanServerName(m));
+                sep=", ";
+            }
+            System.out.println(" ]");
+            final Set<MBeanServer> founds = new HashSet<MBeanServer>();
+            founds.addAll(found);
+            if (!founds.equals(config.getQuery(pat))) {
+                final String msg =
+                        "bad result for findMBeanServerByName(\""+
+                        pat+"\"): expected "+config.getQuery(pat).size()+", "+
+                        "got "+founds.size();
+                throw new Exception(msg);
+            }
+        }
+    }
+
+    public static void testexception(Creator c, String name,
+            Class<? extends Exception> error) throws Exception {
+        Exception failed = null;
+        MBeanServer server = null;
+        try {
+            server = c.create(name);
+        } catch (Exception x) {
+            failed = x;
+        } finally {
+            if (Creator.REFERENCED.contains(c) && server!=null) {
+                MBeanServerFactory.releaseMBeanServer(server);
+            }
+        }
+        if (failed == null && error != null) {
+            throw new Exception("Expected "+error.getName()+
+                    " for "+c+"("+name+")");
+        }
+        if (error != null && !error.isInstance(failed))
+            throw new Exception("Expected "+error.getName()+
+                    " for "+c+"("+name+"), caught "+failed);
+        System.out.println(""+c+"("+name+") PASSED: "+
+                (failed==null?"no exception":String.valueOf(failed)));
+    }
+
+    private static final Map<String,Class<? extends Exception>> failures =
+            new LinkedHashMap<String,Class<? extends Exception>>();
+    private static final Map<String,Class<? extends Exception>> legacy =
+            new LinkedHashMap<String,Class<? extends Exception>>();
+    private static final String[] illegalnames = {
+        "", "-", ":", ";", "?", "*", "wom?bat", "ran:tan.plan",
+        "rin;tin.tin", "tab*mow"
+
+    };
+    private static final String[] legalnames = {
+        "wombat", "top.tip", "ran.tan.plan", "rin.tin.tin!"
+    };
+    private static final String[] nofailures = {
+       MBeanServerFactory.DEFAULT_MBEANSERVER_NAME, "default", null
+    };
+    static {
+        for (String s:illegalnames)
+            failures.put(s, IllegalArgumentException.class);
+        for (String s:nofailures)
+            failures.put(s, null);
+        legacy.putAll(failures);
+        for (String s:legalnames)
+            legacy.put(s, UnsupportedOperationException.class);
+
+    }
+
+    public static void test2(Map<String,Class<? extends Exception>> config)
+        throws Exception {
+        for (Creator c:Creator.NAMED) {
+            for (String s:config.keySet()) testexception(c, s, config.get(s));
+        }
+    }
+
+    public static class LegacyBuilder extends MBeanServerBuilder {
+
+        @Override
+        public MBeanServerDelegate newMBeanServerDelegate() {
+            return new MBeanServerDelegate() {
+                @Override
+                public synchronized String getMBeanServerId() {
+                    return "gloups";
+                }
+            };
+        }
+
+    }
+    public static class LegacyBuilder2 extends MBeanServerBuilder {
+
+        @Override
+        public MBeanServerDelegate newMBeanServerDelegate() {
+            return new MBeanServerDelegate() {
+                @Override
+                public synchronized String getMBeanServerId() {
+                    return "c'est la vie...";
+                }
+                @Override
+                public synchronized void setMBeanServerName(String name) {
+                }
+
+            };
+        }
+
+    }
+
+    public static void test3(Map<String,Class<? extends Exception>> config,
+            String builderClassName)
+        throws Exception {
+        final String builder =
+                System.getProperty("javax.management.builder.initial");
+        System.setProperty("javax.management.builder.initial",
+                builderClassName);
+        try {
+            test2(config);
+        } finally {
+            if (builder != null)
+                System.setProperty("javax.management.builder.initial", builder);
+            else
+                System.clearProperty("javax.management.builder.initial");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        test(test1);
+        test2(failures);
+        test3(legacy,LegacyBuilder.class.getName());
+        test3(legacy,LegacyBuilder2.class.getName());
+    }
+}
--- a/test/javax/management/ObjectName/ApplyWildcardTest.java	Wed Sep 03 14:31:17 2008 +0200
+++ b/test/javax/management/ObjectName/ApplyWildcardTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -28,10 +28,13 @@
  *          with wildcards in the key properties value part.
  * @author Luis-Miguel Alventosa
  * @run clean ApplyWildcardTest
+ * @compile -XDignore.symbol.file=true ApplyWildcardTest.java
  * @run build ApplyWildcardTest
  * @run main ApplyWildcardTest
  */
 
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Util;
 import javax.management.ObjectName;
 
 public class ApplyWildcardTest {
@@ -74,6 +77,75 @@
         { "d:k1=\"a?b\",k2=\"c*d\"", "d:k1=\"axb\",k2=\"cyzd\"" },
         { "d:k1=\"a?b\",k2=\"c*d\",*", "d:k1=\"axb\",k2=\"cyzd\",k3=\"v3\"" },
         { "d:*,k1=\"a?b\",k2=\"c*d\"", "d:k1=\"axb\",k2=\"cyzd\",k3=\"v3\"" },
+
+        // with namespaces
+
+        { "*//:*", "d//:k=v" },
+        { "//?:*", "///:k=v" },
+        { "z*x//:*", "zaxcx//:k=v" },
+        { "*//:*", "d/xx/q//:k=v" },
+        { "z*x//:*", "z/a/x/c/x//:k=v" },
+        { "*x?//:*", "dbdbdxk//:k=v" },
+        { "z*x?x//:*", "zaxcx//:k=v" },
+        { "*x?f//:*", "d/xxf/qxbf//:k=v" },
+        { "z*x?c*x//:*", "z/a/x/c/x//:k=v" },
+
+        { "*//*:*", "d/c/v//x/vgh/:k=v" },
+        { "z*x//z*x:*", "zaxcx//zaxcxcx:k=v" },
+        { "//*//:*", "//d/xx/q//:k=v" },
+        { "z*//*//:*", "z/x/x/z//z/a/x/c/x//:k=v" },
+        { "*x?//blur?g*:*", "dbdbdxk//blurhgblurgh/x/:k=v" },
+        { "z*x??x//??:*", "zaxcxccx///.:k=v" },
+        { "*x?f//?:*", "d/xxf/qxbf///:k=v" },
+        { "z*x?c*x//*//z????//g:*", "z/a/x/c/x//gloubs/././/zargh//g:k=v" },
+        { "z*x?c*x//*//:*", "z/a/x/c/x//gloubs/././/:k=v"},
+        { "*//*//:*", "aza//bzb//:k=v" },
+        { "*//:*", "aza//:k=v" },
+
+        // with or without namespaces, * can also match nothing
+        { "x*z:*", "xz:k=v"},
+
+        { "*//:*", "//:k=v" },
+        { "z*x//:*", "zx//:k=v" },
+        { "*x?//:*", "xk//:k=v" },
+        { "z*x?x//:*", "zxcx//:k=v" },
+        { "*x?f//:*", "xbf//:k=v" },
+        { "z*x?c*x//:*", "zx/cx//:k=v" },
+
+        { "*//*:*", "//:k=v" },
+        { "z*x//z*x:*", "zx//zx:k=v" },
+        { "//*//:*", "////:k=v" },
+        { "z*//*//:*", "z////:k=v" },
+        { "*x?//blur?g*:*", "xk//blurhg:k=v" },
+        { "z*x??x//??:*", "zxccx///.:k=v" },
+        { "*x?f//?:*", "xbf///:k=v" },
+        { "z*x?c*x//*//z????//g:*", "zx/cx////zargh//g:k=v" },
+        { "z*x?c*x//*//:*", "zx/cx////:k=v"},
+        { "*//*//:*", "////:k=v" },
+        { "*//:*", "//:k=v" },
+
+        // recursive namespace meta-wildcard
+        {"**//D:k=v", "a//D:k=v"},
+        {"**//D:k=v", "a//b//c//D:k=v"},
+        {"a//**//D:k=v", "a//b//c//D:k=v"},
+        {"a//**//d//D:k=v", "a//b//c//d//D:k=v"},
+        {"a//**//d//D:k=v", "a//b//c//d//d//D:k=v"},
+        {"a//**//d//D:k=v", "a//a//b//c//d//d//D:k=v"},
+        {"a//**//d//**//e//D:k=v", "a//a//b//d//c//d//e//D:k=v"},
+
+        // special cases for names ending with //
+        { "*:*", "d//:k=v" },
+        { "z*x*:*", "zaxcx//:k=v" },
+        { "*:*", "d/xx/q//:k=v" },
+        { "z*x??:*", "z/a/x/c/x//:k=v" },
+        { "*x???:*", "dbdbdxk//:k=v" },
+        { "z*x?c*x*:*", "z/a/x/c/x//:k=v" },
+        { "?/*/?:*", "d/xx/q//:k=v" },
+        { "**//*:*", "a//b//jmx.rmi:k=v"},
+        { "**//*:*", "a//b//jmx.rmi//:k=v"},
+        { "*//*:*", "wombat//:type=Wombat" },
+        { "**//*:*", "jmx.rmi//:k=v"},
+
     };
 
     private static final String negativeTests[][] = {
@@ -114,6 +186,33 @@
         { "d:k1=\"a?b\",k2=\"c*d\"", "d:k1=\"ab\",k2=\"cd\"" },
         { "d:k1=\"a?b\",k2=\"c*d\",*", "d:k1=\"ab\",k2=\"cd\",k3=\"v3\"" },
         { "d:*,k1=\"a?b\",k2=\"c*d\"", "d:k1=\"ab\",k2=\"cd\",k3=\"v3\"" },
+
+        // with namespaces
+
+        { "z*x?x*:*", "zaxcx//blougs:k=v" },
+        { "*x?f??rata:*", "d/xxf/qxbf//rata:k=v" },
+        { "z*x?c*x*b*:*", "z/a/x/c/x//b//:k=v" },
+
+        { "*:*", "d/c/v//x/vgh/:k=v" },
+        { "z*x??z*x:*", "zaxcx//zaxcxcx:k=v" },
+        { "?/*/?:*", "//d/xx/q//:k=v" },
+        { "z*/?*/?:*", "z/x/x/z//z/a/x/c/x//:k=v" },
+        { "*x?/?blur?g*:*", "dbdbdxk//blurhgblurgh/x/:k=v" },
+        { "z*x??x/???:*", "zaxcxccx///.:k=v" },
+        { "*x?f?/?:*", "d/xxf/qxbf///:k=v" },
+        { "z*x?c*x/?*z????*g:*", "z/a/x/c/x//gloubs/././/zargh//g:k=v" },
+
+        // recursive namespace meta-wildcard
+        {"**//D:k=v", "D:k=v"},
+        {"b//**//D:k=v", "a//b//c//D:k=v"},
+        {"a//**//D:k=v", "a//D:k=v"},
+        {"a//**//d//D:k=v", "a//b//c//d//e//D:k=v"},
+        {"a//**//d//D:k=v", "a//b//c//D:k=v"},
+        {"a//**//d//D:k=v", "a//b//c//d//d//e//D:k=v"},
+        {"a//**//d//**//e//D:k=v", "a//a//b//c//d//e//D:k=v"},
+        {"a//**//d//**//e//D:k=v", "a//a//b//c//e//D:k=v"},
+        { "**//*:*", "jmx.rmi:k=v"},
+
     };
 
     private static int runPositiveTests() {
@@ -129,6 +228,8 @@
                 if (result == false) {
                     error++;
                     System.out.println("Test failed!");
+                    throw new Error("test failed for "+
+                            "\"" + on1 + "\".apply(\"" + on2 + "\")");
                 } else {
                     System.out.println("Test passed!");
                 }
@@ -168,10 +269,85 @@
         return error;
     }
 
+    private static int runRepositoryPositiveTests() {
+        int error = 0;
+        for (int i = 0; i < positiveTests.length; i++) {
+            try {
+                ObjectName on1 = ObjectName.getInstance(positiveTests[i][0]);
+                ObjectName on2 = ObjectName.getInstance(positiveTests[i][1]);
+                if (on1.isPropertyPattern()) {
+                    if (!on1.getKeyPropertyListString().equals("")) continue;
+                } else if (!on1.getCanonicalKeyPropertyListString()
+                            .equals(on2.getCanonicalKeyPropertyListString())) {
+                    continue;
+                }
+                System.out.println("Repository Positive Match Test ---------------");
+                final String dom1 = on1.getDomain();
+                final String dom2 = on2.getDomain();
+                System.out.println("Util.wildpathmatch(\"" + dom2 + "\",\"" + dom1 + "\")");
+                boolean result =
+                        Util.wildpathmatch(dom2,dom1);
+                System.out.println("Result = " + result);
+                if (result == false) {
+                    error++;
+                    System.out.println("Test failed!");
+                } else {
+                    System.out.println("Test passed!");
+                }
+            } catch (Exception e) {
+                error++;
+                System.out.println("Got Unexpected Exception = " + e.toString());
+                System.out.println("Test failed!");
+            }
+            System.out.println("----------------------------------------------");
+        }
+        return error;
+    }
+
+    private static int runRepositoryNegativeTests() {
+        int error = 0;
+        for (int i = 0; i < negativeTests.length; i++) {
+            try {
+                ObjectName on1 = ObjectName.getInstance(negativeTests[i][0]);
+                ObjectName on2 = ObjectName.getInstance(negativeTests[i][1]);
+                if (on1.isPropertyPattern()) {
+                    if (!on1.getKeyPropertyListString().equals("")) continue;
+                } else if (!on1.getCanonicalKeyPropertyListString()
+                            .equals(on2.getCanonicalKeyPropertyListString())) {
+                    continue;
+                }
+                System.out.println("Repository Negative Match Test ---------------");
+                final String dom1 = on1.getDomain();
+                final String dom2 = on2.getDomain();
+                System.out.println("Util.wildpathmatch(\"" + dom2 + "\",\"" + dom1 + "\")");
+                boolean result =
+                        Util.wildpathmatch(dom2,dom1);
+                System.out.println("Result = " + result);
+                if (result == true) {
+                    error++;
+                    System.out.println("Test failed!");
+                } else {
+                    System.out.println("Test passed!");
+                }
+            } catch (Exception e) {
+                error++;
+                System.out.println("Got Unexpected Exception = " + e.toString());
+                System.out.println("Test failed!");
+            }
+            System.out.println("----------------------------------------------");
+        }
+        return error;
+    }
+
     public static void main(String[] args) throws Exception {
 
+
         int error = 0;
 
+        if (!(new ObjectName("z*x*:*").apply(new ObjectName("zaxcx//:k=v"))))
+                throw new Exception();
+
+
         // Check null values
         //
         System.out.println("----------------------------------------------");
@@ -253,6 +429,10 @@
 
         error += runPositiveTests();
         error += runNegativeTests();
+        System.out.println("----------------------------------------------");
+        error += runRepositoryPositiveTests();
+        System.out.println("----------------------------------------------");
+        error += runRepositoryNegativeTests();
 
         if (error > 0) {
             final String msg = "Test FAILED! Got " + error + " error(s)";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/DomainCreationTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test DomainCreationTest.java
+ * @summary Test the creation and registration of JMXDomain instances.
+ * @author Daniel Fuchs
+ * @run clean DomainCreationTest Wombat WombatMBean
+ * @run build DomainCreationTest Wombat WombatMBean
+ * @run main DomainCreationTest
+ */
+
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotificationEmitter;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeMBeanException;
+import javax.management.RuntimeOperationsException;
+import javax.management.namespace.JMXDomain;
+import javax.management.namespace.MBeanServerSupport;
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class DomainCreationTest {
+    private static Map<String,Object> emptyEnvMap() {
+        return Collections.emptyMap();
+    }
+
+
+    public static class LocalDomainRepository
+            extends MBeanServerSupport {
+        private final MBeanServer server;
+        private final String      domain;
+
+
+        public class DynamicMBeanProxy implements DynamicMBean {
+
+            private final MBeanServer server;
+            private final ObjectName name;
+
+            public DynamicMBeanProxy(MBeanServer s, ObjectName n) {
+                this.server = s;
+                this.name = n;
+            }
+
+            public Object getAttribute(String attribute)
+                    throws AttributeNotFoundException,
+                    MBeanException, ReflectionException {
+                try {
+                    return server.getAttribute(name, attribute);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public void setAttribute(Attribute attribute)
+                    throws AttributeNotFoundException,
+                    InvalidAttributeValueException, MBeanException,
+                    ReflectionException {
+                try {
+                    server.setAttribute(name, attribute);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public AttributeList getAttributes(String[] attributes) {
+                try {
+                    return server.getAttributes(name, attributes);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public AttributeList setAttributes(AttributeList attributes) {
+                try {
+                    return server.setAttributes(name, attributes);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public Object invoke(String actionName, Object[] params,
+                    String[] signature) throws MBeanException,
+                    ReflectionException {
+                try {
+                    return server.invoke(name, actionName, params, signature);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public MBeanInfo getMBeanInfo() {
+                try {
+                    return server.getMBeanInfo(name);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+        }
+
+        public LocalDomainRepository(String domain) {
+            this.server = MBeanServerFactory.newMBeanServer();
+            this.domain = domain;
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            try {
+            final ObjectName name =
+                    ObjectName.getInstance(domain+":*");
+            return server.queryNames(name, null);
+            } catch (RuntimeException x) {
+                throw x;
+            } catch (Exception x) {
+                throw new RuntimeException(x);
+            }
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            return new DynamicMBeanProxy(server, name);
+        }
+
+        @Override
+        public NotificationEmitter
+                getNotificationEmitterFor(ObjectName name)
+                throws InstanceNotFoundException {
+            DynamicMBean mbean = getDynamicMBeanFor(name);
+            if (mbean instanceof NotificationEmitter)
+                return (NotificationEmitter) mbean;
+            return null;
+        }
+
+    }
+
+    private static MBeanServer newMBeanServer() {
+        return MBeanServerFactory.newMBeanServer();
+    }
+
+    public static interface ThingMBean {}
+    public static class Thing implements ThingMBean, MBeanRegistration {
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+                throws Exception {
+            if (name == null) return new ObjectName(":type=Thing");
+            else return name;
+        }
+        public void postRegister(Boolean registrationDone) {
+        }
+
+        public void preDeregister() throws Exception {
+        }
+        public void postDeregister() {
+        }
+    }
+
+    /**
+     * Test that it is possible to create a dummy MBean with a null
+     * ObjectName - this is just a sanity check - as there are already
+     * other JMX tests that check that.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testCreateWithNull() throws Exception {
+        final MBeanServer server = newMBeanServer();
+        final ObjectInstance oi = server.registerMBean(new Thing(),null);
+        server.unregisterMBean(oi.getObjectName());
+        System.out.println("testCreateWithNull PASSED");
+    }
+
+    /**
+     * Check that we can register a JMXNamespace MBean, using its standard
+     * ObjectName.
+     * @throws java.lang.Exception
+     */
+    public static void testGoodObjectName() throws Exception {
+        MBeanServer server = newMBeanServer();
+        final ObjectName name =
+                JMXDomain.getDomainObjectName("gloups");
+        final ObjectInstance oi =
+                server.registerMBean(new JMXDomain(
+                new LocalDomainRepository("gloups")),name);
+        System.out.println("Succesfully registered namespace: "+name);
+        try {
+            if (! name.equals(oi.getObjectName()))
+                throw new RuntimeException("testGoodObjectName: TEST failed: " +
+                        "namespace registered as: "+
+                    oi.getObjectName()+" expected: "+name);
+        } finally {
+            server.unregisterMBean(oi.getObjectName());
+        }
+        System.out.println("Succesfully unregistered namespace: "+name);
+        System.out.println("testGoodObjectName PASSED");
+    }
+
+    /**
+     * Check that we cannot register a JMXNamespace MBean, if we don't use
+     * its standard ObjectName.
+     * @throws java.lang.Exception
+     */
+    public static void testBadObjectName() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name = new ObjectName("d:k=v");
+        try {
+            server.registerMBean(new JMXDomain(
+                new LocalDomainRepository("d")),name);
+            System.out.println("testBadObjectName: " +
+                    "Error: MBean registered, no exception thrown.");
+        } catch(RuntimeMBeanException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected RuntimeMBeanException - got "+
+                    x);
+        }
+        if (exp == null)  server.unregisterMBean(name);
+        if (exp == null)
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadObjectName PASSED");
+    }
+
+    /**
+     * Check that we cannot register a Domain MBean in a domain that already
+     * exists.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testBadDomain() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name = new ObjectName("glips:k=v");
+        server.registerMBean(new Wombat(),name);
+
+        final ObjectName dname =
+                JMXDomain.getDomainObjectName("glips");
+
+        try {
+            server.registerMBean(new JMXDomain(
+                new LocalDomainRepository("glips")),dname);
+            System.out.println("testBadDomain: " +
+                    "Error: MBean registered, no exception thrown.");
+        } catch(RuntimeOperationsException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected RuntimeOperationsException - got "+
+                    x);
+        } finally {
+            server.unregisterMBean(name);
+        }
+        if (exp == null)  {
+            server.unregisterMBean(dname);
+        }
+        if (exp == null)
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadDomain PASSED");
+    }
+
+
+    public static void main(String... args) throws Exception {
+        testCreateWithNull();
+        testGoodObjectName();
+        testBadObjectName();
+        testBadDomain();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/EventWithNamespaceControlTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test EventWithNamespaceControlTest.java
+ * @summary Check -Djmx.remote.use.event.service=true and
+ *                -Djmx.remote.delegate.event.service
+ * @author Daniel Fuchs
+ * @run clean EventWithNamespaceTest EventWithNamespaceControlTest
+ *            Wombat WombatMBean JMXRemoteTargetNamespace
+ *            NamespaceController NamespaceControllerMBean
+ * @compile -XDignore.symbol.file=true EventWithNamespaceTest.java
+              EventWithNamespaceControlTest.java
+ *            Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java
+ *            NamespaceController.java NamespaceControllerMBean.java
+ * @run main/othervm -Djmx.remote.use.event.service=true EventWithNamespaceControlTest
+ * @run main/othervm EventWithNamespaceControlTest
+ * @run main/othervm -Djmx.remote.delegate.event.service=false EventWithNamespaceControlTest java.lang.UnsupportedOperationException
+ */
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.logging.Logger;
+import javax.management.RuntimeOperationsException;
+
+/**
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class EventWithNamespaceControlTest extends EventWithNamespaceTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(EventWithNamespaceControlTest.class.getName());
+
+    /** Creates a new instance of EventWithNamespaceTest */
+    public EventWithNamespaceControlTest() {
+    }
+
+
+
+    public static void main(String[] args) {
+        final  EventWithNamespaceControlTest test =
+                new EventWithNamespaceControlTest();
+        if (args.length == 0) {
+            test.run(args);
+            System.out.println("Test successfully passed");
+        } else {
+            try {
+                test.run(args);
+                throw new RuntimeException("Test should have failed.");
+            } catch (RuntimeOperationsException x) {
+                if (! args[0].equals(x.getCause().getClass().getName())) {
+                    System.err.println("Unexpected wrapped exception: "+
+                            x.getCause());
+                    throw x;
+                } else {
+                    System.out.println("Got expected exception: "+x.getCause());
+                }
+            }
+        }
+    }
+
+    public Map<String, ?> getServerMap() {
+        Map<String, ?> retValue = Collections.emptyMap();
+        return retValue;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/EventWithNamespaceTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test EventWithNamespaceTest.java 1.8
+ * @bug 6539857
+ * @summary General Namespace & Notifications test.
+ * @author Daniel Fuchs
+ * @run clean EventWithNamespaceTest Wombat WombatMBean
+ *            JMXRemoteTargetNamespace
+ *            NamespaceController NamespaceControllerMBean
+ * @compile -XDignore.symbol.file=true EventWithNamespaceTest.java
+ *          Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java
+ *          NamespaceController.java NamespaceControllerMBean.java
+ * @run main EventWithNamespaceTest
+ */
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class EventWithNamespaceTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(EventWithNamespaceTest.class.getName());
+
+    /** Creates a new instance of EventWithNamespaceTest */
+    public EventWithNamespaceTest() {
+    }
+
+    private static Map<String,?> singletonMap(String key, Object value) {
+        final Map<String,Object> map = new HashMap<String,Object>();
+        map.put(key,value);
+        return map;
+    }
+
+    public  Map<String,?> getServerMap() {
+        return singletonMap(JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE,"true");
+    }
+
+    public JMXServiceURL export(MBeanServer server)
+    throws Exception {
+        final JMXServiceURL in = new JMXServiceURL("rmi",null,0);
+        final Map<String,?> env = getServerMap();
+
+        final JMXConnectorServer cs =
+                JMXConnectorServerFactory.newJMXConnectorServer(in,env,null);
+        final ObjectName csname = ObjectName.
+                getInstance(cs.getClass().getPackage().getName()+
+                ":type="+cs.getClass().getSimpleName());
+        server.registerMBean(cs,csname);
+        cs.start();
+        return cs.getAddress();
+    }
+
+    public static class Counter {
+        int count;
+        public synchronized int count() {
+            count++;
+            notifyAll();
+            return count;
+        }
+        public synchronized int peek() {
+            return count;
+        }
+        public synchronized int waitfor(int max, long timeout)
+        throws InterruptedException {
+            final long start = System.currentTimeMillis();
+            while (count < max && timeout > 0) {
+                final long rest = timeout -
+                        (System.currentTimeMillis() - start);
+                if (rest <= 0) break;
+                wait(rest);
+            }
+            return count;
+        }
+    }
+
+    public static class CounterListener
+            implements NotificationListener {
+        final private Counter counter;
+        public CounterListener(Counter counter) {
+            this.counter = counter;
+        }
+        public void handleNotification(Notification notification,
+                Object handback) {
+            System.out.println("Received notif from " + handback +
+                    ":\n\t" + notification);
+            if (!notification.getSource().equals(handback)) {
+                System.err.println("OhOh... Unexpected source: \n\t"+
+                        notification.getSource()+"\n\twas expecting:\n\t"+
+                        handback);
+            }
+            counter.count();
+        }
+    }
+
+    public void simpleTest(String[] args) {
+        try {
+            final MBeanServer server1 =
+                    ManagementFactory.getPlatformMBeanServer();
+            final JMXServiceURL url1 = export(server1);
+
+            final MBeanServer server2 =
+                    MBeanServerFactory.createMBeanServer("server2");
+            final JMXServiceURL url2 = export(server2);
+
+            final MBeanServer server3 =
+                    MBeanServerFactory.createMBeanServer("server3");
+            final JMXServiceURL url3 = export(server3);
+
+            final ObjectInstance ncinst =
+                    NamespaceController.createInstance(server1);
+
+            final NamespaceControllerMBean nc =
+                    JMX.newMBeanProxy(server1,ncinst.getObjectName(),
+                    NamespaceControllerMBean.class);
+
+            final String mount2 = nc.mount(url2,"server2",null);
+            final String mount3 = nc.mount(url3,"server2//server3",
+                    null);
+
+            final ObjectName deep =
+                    new ObjectName("server2//server3//bush:type=Wombat,name=kanga");
+            server1.createMBean(Wombat.class.getName(),deep);
+
+            System.err.println("There's a wombat in the bush!");
+
+            final Counter counter = new Counter();
+
+            final NotificationListener listener =
+                    new CounterListener(counter);
+
+            final JMXConnector jc = JMXConnectorFactory.connect(url1);
+            final MBeanServerConnection conn1 =
+                    jc.getMBeanServerConnection();
+            final ObjectName shallow =
+                    new ObjectName("bush:"+
+                    deep.getKeyPropertyListString());
+            final MBeanServerConnection conn2 =
+                    JMXNamespaces.narrowToNamespace(conn1,"server2//server3");
+
+            final WombatMBean proxy1 =
+                    JMX.newMBeanProxy(conn1,deep,WombatMBean.class,true);
+            final WombatMBean proxy2 =
+                    JMX.newMBeanProxy(conn2,shallow,WombatMBean.class,true);
+
+
+            System.err.println("Adding first Notification Listener");
+            conn1.addNotificationListener(deep,listener,null,deep);
+            System.err.println("Adding second Notification Listener");
+            ((NotificationEmitter)proxy2).
+                    addNotificationListener(listener,null,shallow);
+            final JMXConnector c3 = JMXConnectorFactory.connect(url3,
+                    singletonMap(JMXConnector.USE_EVENT_SERVICE,"false"));
+            System.err.println("Adding third Notification Listener");
+            c3.getMBeanServerConnection().
+                    addNotificationListener(shallow,listener,null,shallow);
+            System.err.println("Set attribute to trigger notif");
+            proxy1.setCaption("I am a new Wombat!");
+            System.err.println("Get attribute");
+            System.err.println("New caption: "+proxy2.getCaption());
+            System.err.println("Wait for Notifs...");
+            final int rcvcount = counter.waitfor(3,3000);
+            if (rcvcount != 3)
+                throw new RuntimeException("simpleTest failed: "+
+                        "received count is " +rcvcount);
+            System.err.println("simpleTest: got expected "+rcvcount+
+                    " notifs");
+
+            System.err.println("removing all listeners");
+            conn1.removeNotificationListener(deep,listener,null,deep);
+            ((NotificationEmitter)proxy2)
+                .removeNotificationListener(listener,null,shallow);
+            c3.getMBeanServerConnection().
+                    removeNotificationListener(shallow,listener,null,shallow);
+
+            System.err.println("simpleTest passed: got "+rcvcount+
+                    " notifs");
+
+        } catch (RuntimeException x) {
+            throw x;
+        } catch (Exception x) {
+            throw new RuntimeException("simpleTest failed: " + x,x);
+        }
+    }
+
+    public void run(String[] args) {
+                simpleTest(args);
+    }
+
+    public static void main(String[] args) {
+        new EventWithNamespaceTest().run(args);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/ExportNamespaceTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test ExportNamespaceTest.java
+ * @summary Test that you can export a single namespace through a
+ *          JMXConnectorServer.
+ * @author Daniel Fuchs
+ * @run clean ExportNamespaceTest Wombat WombatMBean
+ * @run build ExportNamespaceTest Wombat WombatMBean
+ * @run main ExportNamespaceTest
+ */
+
+import javax.management.JMX;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class ExportNamespaceTest {
+
+    public static void testExport() throws Exception {
+        final JMXNamespace my =
+                new JMXNamespace(MBeanServerFactory.newMBeanServer());
+        final MBeanServer s = MBeanServerFactory.newMBeanServer();
+        final ObjectName myname = JMXNamespaces.getNamespaceObjectName("my");
+        final ObjectName wname = ObjectName.getInstance("backyard:type=Wombat");
+        my.getSourceServer().registerMBean(new Wombat(),wname);
+        s.registerMBean(my,myname);
+
+        if (!s.queryNames(new ObjectName("my//b*:*"),null).contains(
+                JMXNamespaces.insertPath("my", wname))) {
+            throw new RuntimeException("1: Wombat not found: "+wname);
+        }
+
+        final MBeanServer cd = JMXNamespaces.narrowToNamespace(s, "my");
+        if (!cd.queryNames(new ObjectName("b*:*"),null).contains(wname)) {
+            throw new RuntimeException("2: Wombat not found: "+wname);
+        }
+
+        final JMXServiceURL url = new JMXServiceURL("rmi",null,0);
+        final JMXConnectorServer server =
+                JMXConnectorServerFactory.newJMXConnectorServer(url, null, cd);
+        server.start();
+
+        final JMXConnector jc = JMXConnectorFactory.
+                connect(server.getAddress(),null);
+        final MBeanServerConnection mbsc = jc.getMBeanServerConnection();
+
+        if (!mbsc.queryNames(new ObjectName("b*:*"),null).contains(wname)) {
+            throw new RuntimeException("3: Wombat not found: "+wname);
+        }
+        System.out.println("Found a Wombat in my backyard.");
+
+        final String deepThoughts = "I want to leave this backyard!";
+        final WombatMBean w = JMX.newMBeanProxy(mbsc, wname, WombatMBean.class);
+        w.setCaption(deepThoughts);
+        if (!deepThoughts.equals(w.getCaption()))
+                throw new RuntimeException("4: Wombat is not thinking right: "+
+                        w.getCaption());
+
+    }
+
+    public static void main(String... args) throws Exception {
+        testExport();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXDomainTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,512 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test JMXDomainTest.java
+ * @summary Basic test for JMXDomain.
+ * @author Daniel Fuchs
+ * @run clean JMXDomainTest Wombat WombatMBean
+ * @run build JMXDomainTest Wombat WombatMBean
+ * @run main JMXDomainTest
+ */
+
+
+import java.util.Collections;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.Map;
+import java.util.Set;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanServerNotification;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.namespace.JMXDomain;
+import javax.management.namespace.MBeanServerSupport;
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class JMXDomainTest {
+    private static Map<String,Object> emptyEnvMap() {
+        return Collections.emptyMap();
+    }
+
+
+    public static class LocalDomainRepository
+            extends MBeanServerSupport {
+        private final MBeanServer server;
+        private final String      domain;
+
+        public class DynamicMBeanProxy implements DynamicMBean {
+
+            private final MBeanServer server;
+            private final ObjectName name;
+
+            public DynamicMBeanProxy(MBeanServer s, ObjectName n) {
+                this.server = s;
+                this.name = n;
+            }
+
+            public Object getAttribute(String attribute)
+                    throws AttributeNotFoundException,
+                    MBeanException, ReflectionException {
+                try {
+                    return server.getAttribute(name, attribute);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public void setAttribute(Attribute attribute)
+                    throws AttributeNotFoundException,
+                    InvalidAttributeValueException, MBeanException,
+                    ReflectionException {
+                try {
+                    server.setAttribute(name, attribute);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public AttributeList getAttributes(String[] attributes) {
+                try {
+                    return server.getAttributes(name, attributes);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public AttributeList setAttributes(AttributeList attributes) {
+                try {
+                    return server.setAttributes(name, attributes);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public Object invoke(String actionName, Object[] params,
+                    String[] signature) throws MBeanException,
+                    ReflectionException {
+                try {
+                    return server.invoke(name, actionName, params, signature);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            public MBeanInfo getMBeanInfo() {
+                try {
+                    return server.getMBeanInfo(name);
+                } catch (RuntimeException x) {
+                    throw x;
+                } catch (Exception x) {
+                    throw new RuntimeException(x);
+                }
+            }
+        }
+
+        public LocalDomainRepository(String domain) {
+            this.server = MBeanServerFactory.newMBeanServer();
+            this.domain = domain;
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            try {
+            final ObjectName name =
+                    ObjectName.getInstance(domain+":*");
+            return server.queryNames(name, null);
+            } catch (RuntimeException x) {
+                throw x;
+            } catch (Exception x) {
+                throw new RuntimeException(x);
+            }
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            if (server.isRegistered(name))
+                return new DynamicMBeanProxy(server, name);
+            throw new InstanceNotFoundException(name);
+        }
+
+
+        @Override
+        public NotificationEmitter
+                getNotificationEmitterFor(final ObjectName name)
+                throws InstanceNotFoundException {
+            if (server.isInstanceOf(name, NotificationEmitter.class.getName())) {
+                return new NotificationEmitter() {
+
+                    public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
+                        try {
+                            server.removeNotificationListener(name, listener, filter, handback);
+                        } catch (InstanceNotFoundException x) {
+                            throw new IllegalArgumentException(String.valueOf(name), x);
+                        }
+                    }
+
+                    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
+                        try {
+                            server.addNotificationListener(name, listener, filter, handback);
+                        } catch (InstanceNotFoundException x) {
+                            throw new IllegalArgumentException(String.valueOf(name), x);
+                        }
+                    }
+
+                    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
+                        try {
+                            server.removeNotificationListener(name, listener);
+                        } catch (InstanceNotFoundException x) {
+                            throw new IllegalArgumentException(String.valueOf(name), x);
+                        }
+                    }
+
+                    public MBeanNotificationInfo[] getNotificationInfo() {
+                        try {
+                            return server.getMBeanInfo(name).getNotifications();
+                        } catch (Exception x) {
+                            throw new IllegalArgumentException(String.valueOf(name), x);
+                        }
+                    }
+                };
+            }
+            return null;
+        }
+
+        @Override
+        public ObjectInstance registerMBean(Object object, ObjectName name)
+                throws InstanceAlreadyExistsException,
+                MBeanRegistrationException, NotCompliantMBeanException {
+            return server.registerMBean(object, name);
+        }
+
+        @Override
+        public void unregisterMBean(ObjectName name)
+                throws InstanceNotFoundException,
+                MBeanRegistrationException {
+            server.unregisterMBean(name);
+        }
+
+        @Override
+        public ObjectInstance createMBean(String className,
+                ObjectName name, ObjectName loaderName, Object[] params,
+                String[] signature, boolean useCLR)
+                throws ReflectionException, InstanceAlreadyExistsException,
+                MBeanRegistrationException, MBeanException,
+                NotCompliantMBeanException, InstanceNotFoundException {
+            if (useCLR && loaderName == null) {
+                return server.createMBean(className, name, params, signature);
+            }
+            return server.createMBean(className, name, loaderName,
+                    params, signature);
+        }
+
+
+    }
+
+    private static MBeanServer newMBeanServer() {
+        return MBeanServerFactory.newMBeanServer();
+    }
+
+    public static interface ThingMBean {}
+    public static class Thing implements ThingMBean, MBeanRegistration {
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+                throws Exception {
+            if (name == null) return new ObjectName(":type=Thing");
+            else return name;
+        }
+        public void postRegister(Boolean registrationDone) {
+        }
+
+        public void preDeregister() throws Exception {
+        }
+        public void postDeregister() {
+        }
+    }
+
+    /**
+     * Test that it is possible to create a dummy MBean with a null
+     * ObjectName - this is just a sanity check - as there are already
+     * other JMX tests that check that.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testCreateWithNull() throws Exception {
+        final MBeanServer server = newMBeanServer();
+        final ObjectInstance oi = server.registerMBean(new Thing(),null);
+        server.unregisterMBean(oi.getObjectName());
+        System.out.println("testCreateWithNull PASSED");
+    }
+
+    public static void testRegisterSimple() throws Exception {
+        final ObjectName name =
+                JMXDomain.getDomainObjectName("gloups");
+        final JMXDomain jmxDomain = new JMXDomain(
+                MBeanServerFactory.newMBeanServer());
+        testRegister("testRegisterSimple: ",name,jmxDomain);
+    }
+
+    public static void testRegisterPseudoVirtual()
+            throws Exception {
+        final ObjectName name =
+                JMXDomain.getDomainObjectName("gloups");
+        final JMXDomain jmxDomain = new JMXDomain(
+                new LocalDomainRepository("gloups"));
+        testRegister("testRegisterPseudoVirtual: ",name,jmxDomain);
+    }
+
+    public static void testRegister(final String test,
+            final ObjectName name,
+            final JMXDomain jmxDomain) throws Exception {
+        System.out.println(test+" START");
+        MBeanServer server = newMBeanServer();
+        final ObjectInstance oi =
+                server.registerMBean(jmxDomain,name);
+        System.out.println(test+"Succesfully registered namespace: "+name);
+        if (!server.isRegistered(name))
+            fail(test+name+" is not registered!");
+        if (!server.queryNames(new ObjectName(name.getDomain()+":*"), null).
+                contains(name))
+            fail(test+name+" not in queryNames");
+
+        final Thing thing = new Thing();
+        final ObjectName thingName = new ObjectName("gloups:type=Thing");
+        server.registerMBean(thing,thingName);
+        if (!server.isRegistered(thingName))
+            fail(test+thingName+" is not registered!");
+        if (!jmxDomain.getSourceServer().isRegistered(thingName))
+            fail(test+thingName+" is not registered in domain!");
+        if (!server.queryNames(new ObjectName(name.getDomain()+":*"), null).
+                contains(thingName))
+            fail(test+thingName+" not in queryNames");
+
+        server.unregisterMBean(name);
+        if (server.isRegistered(thingName))
+            fail(test+thingName+" is still registered!");
+        if (server.queryNames(new ObjectName(name.getDomain()+":*"), null).
+                contains(thingName))
+            fail(test+thingName+" still in queryNames");
+
+        server.registerMBean(jmxDomain, name);
+        if (!server.isRegistered(thingName))
+            fail(test+thingName+" is not registered again!");
+
+        System.out.println(test+" PASSED");
+    }
+
+    private static MBeanServerNotification pop(
+            BlockingQueue<Notification> queue,
+                                    String type,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        final Notification n = queue.poll(1, TimeUnit.SECONDS);
+        if (!(n instanceof MBeanServerNotification))
+            fail(test+"expected MBeanServerNotification, got "+n);
+        final MBeanServerNotification msn = (MBeanServerNotification)n;
+        if (!type.equals(msn.getType()))
+            fail(test+"expected "+type+", got "+msn.getType());
+        if (!mbean.apply(msn.getMBeanName()))
+            fail(test+"expected "+mbean+", got "+msn.getMBeanName());
+        System.out.println(test+" got: "+msn);
+        return msn;
+    }
+    private static MBeanServerNotification popADD(
+            BlockingQueue<Notification> queue,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        return pop(queue, MBeanServerNotification.REGISTRATION_NOTIFICATION,
+                mbean, test);
+    }
+
+    private static MBeanServerNotification popREM(
+            BlockingQueue<Notification> queue,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        return pop(queue, MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
+                mbean, test);
+    }
+
+
+    public static void testRegisterNotifSimple() throws Exception {
+        final ObjectName name =
+                JMXDomain.getDomainObjectName("gloups");
+        final JMXDomain jmxDomain = new JMXDomain(
+                MBeanServerFactory.newMBeanServer());
+        testRegisterNotif("testRegisterNotifSimple: ",name,jmxDomain);
+    }
+
+    public static void testRegisterNotifPseudoVirtual()
+            throws Exception {
+        final ObjectName name =
+                JMXDomain.getDomainObjectName("gloups");
+        final JMXDomain jmxDomain = new JMXDomain(
+                new LocalDomainRepository("gloups"));
+        testRegisterNotif("testRegisterNotifPseudoVirtual: ",name,jmxDomain);
+    }
+
+    public static void testRegisterNotif(final String test,
+            final ObjectName name,
+            final JMXDomain jmxDomain) throws Exception {
+        System.out.println(test+" START");
+        MBeanServer server = newMBeanServer();
+        final ObjectInstance oi =
+                server.registerMBean(jmxDomain,name);
+        System.out.println(test+"Succesfully registered namespace: "+name);
+        if (!server.isRegistered(name))
+            fail(test+name+" is not registered!");
+
+        final BlockingQueue<Notification> queue =
+                new ArrayBlockingQueue<Notification>(10);
+
+        final NotificationListener l = new NotificationListener() {
+
+            public void handleNotification(Notification notification,
+                    Object handback) {
+                try {
+                    if (!queue.offer(notification,5,TimeUnit.SECONDS))
+                        throw new RuntimeException("timeout exceeded");
+                } catch (Exception x) {
+                    fail(test+"failed to handle notif", x);
+                }
+            }
+        };
+
+        server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l,
+                null, null);
+
+        final Thing thing = new Thing();
+        final ObjectName thingName = new ObjectName("gloups:type=Thing");
+
+        server.registerMBean(thing,thingName);
+        if (!jmxDomain.getSourceServer().isRegistered(thingName))
+            fail(test+thingName+" is not registered in domain!");
+        popADD(queue, thingName, test);
+        server.unregisterMBean(thingName);
+        if (jmxDomain.getSourceServer().isRegistered(thingName))
+            fail(test+thingName+" is still registered in domain!");
+        popREM(queue, thingName, test);
+        if (queue.size() != 0)
+            fail(test+queue.size()+" notifs remain in queue "+queue);
+
+        server.unregisterMBean(name);
+        popREM(queue, name, test);
+
+        jmxDomain.getSourceServer().registerMBean(thing,thingName);
+        if (server.isRegistered(thingName))
+            fail(test+thingName+" is still registered in domain!");
+        jmxDomain.getSourceServer().unregisterMBean(thingName);
+        if (queue.size() != 0)
+            fail(test+queue.size()+" notifs remain in queue "+queue);
+
+        server.registerMBean(jmxDomain, name);
+        if (!server.isRegistered(name))
+            fail(test+name+" is not registered again!");
+        popADD(queue, name, test);
+        if (queue.size() != 0)
+            fail(test+queue.size()+" notifs remain in queue "+queue);
+
+        server.registerMBean(thing,thingName);
+        if (!jmxDomain.getSourceServer().isRegistered(thingName))
+            fail(test+thingName+" is not registered in domain!");
+        popADD(queue, thingName, test);
+        server.unregisterMBean(thingName);
+        if (jmxDomain.getSourceServer().isRegistered(thingName))
+            fail(test+thingName+" is still registered in domain!");
+        popREM(queue, thingName, test);
+        if (queue.size() != 0)
+            fail(test+queue.size()+" notifs remain in queue "+queue);
+
+        System.out.println(test+" PASSED");
+    }
+
+
+
+    private static void fail(String msg) {
+        raise(new RuntimeException(msg));
+    }
+
+    private static void fail(String msg, Throwable cause) {
+        raise(new RuntimeException(msg,cause));
+    }
+
+    private static void raise(RuntimeException x) {
+        lastException = x;
+        exceptionCount++;
+        throw x;
+    }
+
+    private static volatile Exception lastException = null;
+    private static volatile int       exceptionCount = 0;
+
+    public static void main(String... args) throws Exception {
+        testCreateWithNull();
+
+        testRegisterSimple();
+        testRegisterNotifSimple();
+
+        testRegisterPseudoVirtual();
+        testRegisterNotifPseudoVirtual();
+
+        if (lastException != null)
+            throw lastException;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXNamespaceSecurityTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test JMXNamespaceSecurityTest.java
+ * @summary General JMXNamespaceSecurityTest test.
+ * @author Daniel Fuchs
+ * @run clean JMXNamespaceViewTest JMXNamespaceSecurityTest Wombat WombatMBean
+ *            LazyDomainTest
+ * @run build JMXNamespaceSecurityTest JMXNamespaceViewTest Wombat WombatMBean
+ *            LazyDomainTest
+ * @run main/othervm JMXNamespaceSecurityTest namespace.policy
+ */
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXDomain;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXConnectorServer;
+
+/**
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class JMXNamespaceSecurityTest extends JMXNamespaceViewTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(JMXNamespaceSecurityTest.class.getName());
+
+    public static class NamedMBeanServerCreator
+            extends JMXNamespaceViewTest.MBeanServerConfigCreator {
+      public MBeanServer createMBeanServerFor(NamespaceConfig config) {
+            return MBeanServerFactory.
+                    createNamedMBeanServer(config.name,config.name);
+        }
+    }
+    /**
+     * Creates a config for a hierarchy of namespaces, mixing local namespaces
+     * and remote namespaces using the given protocol.
+     * @param protocol The protocol that should be used for remote namespaces.
+     * @return A namespace config hierarchy.
+     * @throws java.lang.Exception
+     */
+    public static NamespaceConfig[] makeConfig(String protocol)
+        throws Exception {
+        final NamespaceConfig[] config = {
+        // Top level namespace "top1" (local)
+        config("top1",wombats("wchief","w1","w2","w3"),
+                // top1//local1
+                config("local1",wombats("wchief","ww1","ww2")),
+                // top1//local2
+                config("local2",wombats("wchief","ww4","ww5","ww6"),
+                    // top1//local2//local3
+                    config("local3",wombats("wchief","www1","www2")),
+                    // top1//local2//rmi1
+                    config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))),
+                // top1//rmi2
+                config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"),
+                    // top1//rmi2//local4
+                    config("local4",wombats("wchief","www6","www7")),
+                    // top1//rmi2//rmi3
+                    config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"),
+                        // top1//rmi2//rmi3//local5
+                        config("local5",wombats("wchief","wwww1"))))),
+        // Top level namespace "top2" (local)
+        config("top2",wombats("wchief","w21","w22","w23"),
+                // top2//local21
+                config("local21",wombats("wchief","ww21","ww22")),
+                // top2//rmi22
+                config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"),
+                    // top2//rmi22//local24
+                    config("local24",wombats("wchief","www26","www27")),
+                    // top2//rmi22//rmi23
+                    config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"),
+                        // top2//rmi22//rmi23//local25
+                        config("local25",wombats("wchief","wwww21"))))),
+        // Top level namespace "top3" (remote)
+        config("top3",url(protocol),wombats("wchief","w31","w32","w33"),
+                // top3//local31
+                config("local31",wombats("wchief","ww31","ww32")),
+                // top3//rmi32
+                config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"),
+                    // top3//rmi32//local34
+                    config("local34",wombats("wchief","www36","www37")),
+                    // top3//rmi32//rmi33
+                    config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"),
+                        // top3//rmi32//local35
+                        config("local35",wombats("wchief","wwww31"))))),
+        };
+        return config;
+    }
+
+    public static void test(MBeanServer server, NamespaceConfig[] namespaces)
+        throws Exception {
+        System.out.println("Launching test...");
+        List<JMXConnectorServer> cslist = load(server,
+                new NamedMBeanServerCreator(), namespaces);
+        Map<String,NamespaceConfig> inputMap =
+                new HashMap<String,NamespaceConfig>();
+
+        for (NamespaceConfig cfg : namespaces) {
+            fillMap(inputMap,"",cfg);
+        }
+        final MBeanServer platform = ManagementFactory.getPlatformMBeanServer();
+        //if (System.getProperty("jmx.wait")!=null) {
+            /*
+            // if we wanted to lazy load the platform MBeanServer:
+            final LazyDomainTest.MBeanServerLoader loader =
+                    new LazyDomainTest.MBeanServerLoader() {
+                public MBeanServer loadMBeanServer() {
+                    return ManagementFactory.getPlatformMBeanServer();
+                }
+            };
+            final LazyDomainTest.MBeanServerProxy proxy =
+                    new LazyDomainTest.MBeanServerProxy(loader);
+            final LazyDomainTest.LazyDomain domain =
+                    new LazyDomainTest.LazyDomain(proxy);
+            server.registerMBean(domain,
+                    JMXDomain.getDomainObjectName("java.lang"));
+            */
+            // Mount java.lang MBeans into our private server so that
+            // visualvm can connect.
+            server.registerMBean(
+                    new JMXDomain(platform),
+                    JMXDomain.getDomainObjectName("java.lang"));
+        //}
+        if (System.getProperty("jmx.wait")!=null) {
+            platform.registerMBean(new JMXNamespace(server),
+                    JMXNamespaces.getNamespaceObjectName("test"));
+        }
+
+        System.setSecurityManager(new SecurityManager());
+
+        // Some sanity checks... The policy file should allow access
+        // to java.lang MBeans.
+        final ObjectName platnames = new ObjectName("java.lang:*");
+        for (ObjectName o : platform.queryNames(platnames,null)) {
+            server.getMBeanInfo(o);
+        }
+        final Set<ObjectName> lang =
+                new HashSet<ObjectName>(server.queryNames(platnames, null));
+        lang.remove(JMXDomain.getDomainObjectName("java.lang"));
+        if (!lang.equals(platform.
+                queryNames(platnames, null)))
+            throw new Exception("Wrong list of platform names: "+lang);
+        System.out.println("Got all java.lang MBeans: "+lang);
+
+        // The policy file should allow to see all namespaces.
+        // check this...
+        final List<ObjectName> patterns = new ArrayList<ObjectName>();
+        final Set<String> paths = new TreeSet<String>();
+        final Set<String> uuids = new HashSet<String>();
+        patterns.add(new ObjectName("*//:*"));
+        while (patterns.size()>0) {
+            System.out.println("server.queryNames("+patterns.get(0)+",null)");
+            Set<ObjectName> names = server.queryNames(patterns.remove(0),null);
+            System.out.println("found: "+names);
+
+            for (ObjectName no : names) {
+                final String uuid = (String) server.getAttribute(no, "UUID");
+                if (uuids.contains(uuid)) {
+                    System.out.print("namespace "+no+", uuid="+uuid+
+                            " already parsed. Skipping");
+                    continue;
+                }
+                uuids.add(uuid);
+                patterns.add(new ObjectName(no.getDomain()+"*//:*"));
+                System.out.println("added pattern: "+
+                        new ObjectName(no.getDomain()+"*//:*"));
+                if (no.getDomain().endsWith(ClientContext.NAMESPACE+
+                        JMXNamespaces.NAMESPACE_SEPARATOR)) continue;
+                paths.add(no.getDomain().substring(0,
+                        no.getDomain().length()-
+                        JMXNamespaces.NAMESPACE_SEPARATOR.length()));
+            }
+        }
+        final TreeSet<String> expected = new TreeSet<String>(inputMap.keySet());
+        if (!expected.equals(paths)) {
+            throw new Exception("wrong set of namespaces, expected "+
+                    expected+", got "+paths);
+        }
+
+        System.out.println("Got all namespaces: "+paths);
+
+        // Check that we can see all wombats.
+        //
+        ObjectName wchief =
+                new ObjectName("top1//rmi2//wombat:name=wchief,type=Wombat");
+        String caption = (String) server.getAttribute(wchief,"Caption");
+        System.out.println("wchief says "+caption);
+        Object mood = server.getAttribute(wchief,"Mood");
+        System.out.println("wchief's mood on a scale of 100 is "+mood);
+
+        ObjectName wchief2 =
+                new ObjectName("top1//wombat:name=wchief,type=Wombat");
+        String caption2 = (String) server.getAttribute(wchief2,"Caption");
+        System.out.println("wchief2 says "+caption2);
+        try {
+            Object mood2 = server.getAttribute(wchief2,"Mood");
+            System.out.println("wchief2's mood on a scale of 100 is "+mood2);
+            throw new Exception("Expected security exception for "+
+                    "getAttribute("+wchief2+", \"Mood\"");
+        } catch (SecurityException x) {
+            System.out.println("wchief2's mood is unavailable: "+x);
+        }
+        try {
+            exportAndWaitIfNeeded(server);
+        } finally {
+            closeAll(cslist);
+        }
+
+    }
+    /** Creates a new instance of JMXNamespaceTest */
+    public JMXNamespaceSecurityTest() {
+    }
+
+    public static void main(String[] args) throws Exception {
+        String osName = System.getProperty("os.name");
+        System.out.println("os.name = " + osName);
+        if (!osName.equals("SunOS")) {
+            System.out.println("This test runs on Solaris only.");
+            System.out.println("Bye! Bye!");
+            return;
+        }
+        final String policy = System.getProperty("test.src") +
+                File.separator + args[0];
+        System.out.println("PolicyFile = " + policy);
+        System.setProperty("java.security.policy", policy);
+        if (!new File(System.getProperty("java.security.policy")).canRead())
+            throw new IOException("no such file: "+
+                    System.getProperty("java.security.policy"));
+        test(MBeanServerFactory.createNamedMBeanServer("root","root"),
+                makeConfig("rmi"));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXNamespaceTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,714 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test JMXNamespaceTest.java
+ * @summary General JMXNamespace test.
+ * @author Daniel Fuchs
+ * @run clean JMXNamespaceTest
+ *            Wombat WombatMBean JMXRemoteTargetNamespace
+ *            NamespaceController NamespaceControllerMBean
+ * @compile -XDignore.symbol.file=true JMXNamespaceTest.java
+ *            Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java
+ *            NamespaceController.java NamespaceControllerMBean.java
+ * @run main/othervm JMXNamespaceTest
+ */
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.NotificationEmitter;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaceMBean;
+import javax.management.namespace.JMXRemoteNamespaceMBean;
+import javax.management.namespace.MBeanServerConnectionWrapper;
+import javax.management.namespace.MBeanServerSupport;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class JMXNamespaceTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(JMXNamespaceTest.class.getName());
+
+    /** Creates a new instance of JMXNamespaceTest */
+    public JMXNamespaceTest() {
+    }
+
+    public static class WombatRepository extends MBeanServerSupport {
+        final Wombat wombat;
+        final StandardMBean mbean;
+        final ObjectName wombatName;
+
+        public WombatRepository(ObjectName wombatName) {
+            try {
+                wombat = new Wombat();
+                mbean  = wombat;
+                this.wombatName = wombatName;
+                wombat.preRegister(null,wombatName);
+            } catch (Exception x) {
+                throw new IllegalArgumentException(x);
+            }
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+            throws InstanceNotFoundException {
+            if (wombatName.equals(name)) return mbean;
+            else throw new InstanceNotFoundException(String.valueOf(name));
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            final Set<ObjectName> res = Collections.singleton(wombatName);
+            return res;
+        }
+
+        @Override
+        public NotificationEmitter
+                getNotificationEmitterFor(ObjectName name)
+            throws InstanceNotFoundException {
+            final DynamicMBean mb = getDynamicMBeanFor(name);
+            if (mb instanceof NotificationEmitter)
+                return (NotificationEmitter)mb;
+            return null;
+        }
+    }
+
+    public static class SimpleTest {
+            public final String   descr;
+            private final Class<?> testClass;
+            private final Method method;
+            public SimpleTest(String descr) {
+                this.descr = descr;
+                this.testClass = JMXNamespaceTest.class;
+                try {
+                    method = testClass.
+                        getDeclaredMethod(descr,SimpleTestConf.class,
+                            Object[].class);
+                } catch (NoSuchMethodException x) {
+                    throw new IllegalArgumentException(descr+": test not found",
+                            x);
+                }
+            }
+
+            public void run(SimpleTestConf conf, Object... args)
+                throws Exception {
+                try {
+                    method.invoke(null,conf,args);
+                } catch (InvocationTargetException x) {
+                    final Throwable cause = x.getCause();
+                    if (cause instanceof Exception) throw (Exception)cause;
+                    if (cause instanceof Error) throw (Error)cause;
+                    throw x;
+                }
+            }
+    }
+
+    private static class SimpleTestConf {
+        public final  Wombat wombat;
+        public final  StandardMBean mbean;
+        public final  String dirname;
+        public final  ObjectName handlerName;
+        public final  ObjectName wombatNickName;
+        public final  ObjectName wombatName;
+        public final  JMXNamespace wombatNamespace;
+        public final  MBeanServer server;
+        public final  WombatMBean proxy;
+        public SimpleTestConf(String[] args) throws Exception {
+            wombat = new Wombat();
+            mbean = wombat;
+            dirname = "wombat";
+            handlerName =
+                    new ObjectName(dirname+"//:type=JMXNamespace");
+
+            wombatNickName =
+                    new ObjectName("burrow:type=Wombat");
+
+            wombatName =
+                    new ObjectName(dirname+"//"+wombatNickName);
+
+            wombatNamespace =
+                    new JMXNamespace(
+                    new WombatRepository(wombatNickName));
+
+            server = ManagementFactory.getPlatformMBeanServer();
+            System.out.println(handlerName+" registered="+
+                    server.isRegistered(handlerName));
+            server.registerMBean(wombatNamespace,handlerName);
+
+            try {
+                proxy = JMX.newMBeanProxy(server,wombatName,
+                                WombatMBean.class);
+            } catch (Exception x) {
+                server.unregisterMBean(handlerName);
+                throw x;
+            }
+        }
+
+        public void close() {
+            try {
+                server.unregisterMBean(handlerName);
+            } catch (Exception x) {
+                System.out.println("Failed to close: " + x);
+                x.printStackTrace();
+            }
+        }
+
+        public void test(SimpleTest test,Object... args)
+            throws Exception {
+            try {
+                test.run(this,args);
+                passed++;
+            } catch (Exception x) {
+                failed++;
+                System.err.println(test.descr+" failed: " + x);
+                x.printStackTrace();
+            }
+        }
+
+        public volatile int failed = 0;
+        public volatile int passed = 0;
+    }
+
+    static void checkValue(String name,Object expected, Object returned)
+        throws InvalidAttributeValueException {
+        if (Collections.singletonList(expected).
+                equals(Collections.singletonList(returned))) return;
+
+        throw new InvalidAttributeValueException("Bad value for "+
+                name+": ["+returned+"] - was expecting ["+expected+"]");
+    }
+
+    // ---------------------------------------------------------------
+    // SIMPLE TESTS BEGIN HERE
+    // ---------------------------------------------------------------
+
+    static void getCaptionTest(SimpleTestConf env, Object... args)
+        throws Exception {
+        System.out.println(env.proxy.getCaption());
+    }
+
+    static void setCaptionTest(SimpleTestConf env, Object... args)
+        throws Exception {
+        env.proxy.setCaption((String)args[0]);
+        final String result = env.proxy.getCaption();
+        System.out.println(result);
+        checkValue("Caption",args[0],result);
+    }
+
+    static void queryNamesTest1(SimpleTestConf env, Object... args)
+        throws Exception {
+        final ObjectName pat =
+                new ObjectName(env.handlerName.getDomain()+"*:*");
+        final Set<ObjectName> res =
+                env.server.queryNames(pat,null);
+        System.out.println("queryNamesTest1: "+res);
+        checkValue("names",Collections.singleton(env.wombatName),res);
+    }
+
+    static void queryNamesTest2(SimpleTestConf env, Object... args)
+        throws Exception {
+        final ObjectName pat =
+                new ObjectName("*:"+
+                env.wombatName.getKeyPropertyListString());
+        final Set<ObjectName> res =
+                env.server.queryNames(pat,null);
+        System.out.println("queryNamesTest2: "+res);
+        checkValue("names",Collections.emptySet(),res);
+    }
+
+    static void getDomainsTest(SimpleTestConf env, Object... args)
+        throws Exception {
+        final List<String> domains =
+                Arrays.asList(env.server.getDomains());
+        System.out.println("getDomainsTest: "+domains);
+        if (domains.contains(env.wombatName.getDomain()))
+            throw new InvalidAttributeValueException("domain: "+
+                    env.wombatName.getDomain());
+        if (!domains.contains(env.handlerName.getDomain()))
+            throw new InvalidAttributeValueException("domain not found: "+
+                    env.handlerName.getDomain());
+    }
+
+    // ---------------------------------------------------------------
+    // SIMPLE TESTS END HERE
+    // ---------------------------------------------------------------
+
+    private static void simpleTest(String[] args) {
+        final SimpleTestConf conf;
+        try {
+            conf = new SimpleTestConf(args);
+            try {
+                conf.test(new SimpleTest("getCaptionTest"));
+                conf.test(new SimpleTest("setCaptionTest"),
+                        "I am a new Wombat!");
+                conf.test(new SimpleTest("queryNamesTest1"));
+                conf.test(new SimpleTest("queryNamesTest2"));
+                conf.test(new SimpleTest("getDomainsTest"));
+            } finally {
+                conf.close();
+            }
+        } catch (Exception x) {
+            System.err.println("simpleTest FAILED: " +x);
+            x.printStackTrace();
+            throw new RuntimeException(x);
+        }
+        System.out.println("simpleTest: "+conf.passed+
+                " PASSED, " + conf.failed + " FAILED.");
+        if (conf.failed>0) {
+            System.err.println("simpleTest FAILED ["+conf.failed+"]");
+            throw new RuntimeException("simpleTest FAILED ["+conf.failed+"]");
+        } else {
+            System.err.println("simpleTest PASSED ["+conf.passed+"]");
+        }
+    }
+
+    public static void recursiveTest(String[] args) {
+        final SimpleTestConf conf;
+        try {
+            conf = new SimpleTestConf(args);
+            try {
+                final JMXServiceURL url =
+                        new JMXServiceURL("rmi","localHost",0);
+                final Map<String,Object> empty = Collections.emptyMap();
+                final JMXConnectorServer server =
+                        JMXConnectorServerFactory.newJMXConnectorServer(url,
+                        empty,conf.server);
+                server.start();
+                final JMXServiceURL address = server.getAddress();
+                final JMXConnector client =
+                        JMXConnectorFactory.connect(address,
+                        empty);
+                final String[] signature = {
+                    JMXServiceURL.class.getName(),
+                    Map.class.getName(),
+                };
+                final String[] signature2 = {
+                    JMXServiceURL.class.getName(),
+                    Map.class.getName(),
+                    String.class.getName(),
+                };
+                final Object[] params = {
+                    address,
+                    null,
+                };
+                final MBeanServerConnection c =
+                        client.getMBeanServerConnection();
+                final ObjectName dirName1 =
+                        new ObjectName("kanga//:type=JMXNamespace");
+                c.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                              dirName1, params,signature);
+                c.invoke(dirName1, "connect", null, null);
+                try {
+                    final MemoryMXBean memory =
+                            JMX.newMXBeanProxy(c,
+                            new ObjectName("kanga//"+
+                            ManagementFactory.MEMORY_MXBEAN_NAME),
+                            MemoryMXBean.class);
+                    System.out.println("HeapMemory #1: "+
+                            memory.getHeapMemoryUsage().toString());
+                    final MemoryMXBean memory2 =
+                            JMX.newMXBeanProxy(c,
+                            new ObjectName("kanga//kanga//"+
+                            ManagementFactory.MEMORY_MXBEAN_NAME),
+                            MemoryMXBean.class);
+                    System.out.println("HeapMemory #2: "+
+                            memory2.getHeapMemoryUsage().toString());
+                    final Object[] params2 = {
+                        address,
+                        null,
+                        "kanga//kanga"
+                        // "kanga//kanga//roo//kanga", <= cycle
+                    };
+                    final ObjectName dirName2 =
+                            new ObjectName("kanga//roo//:type=JMXNamespace");
+                    c.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                              dirName2, params2, signature2);
+                    System.out.println(dirName2 + " created!");
+                    JMX.newMBeanProxy(c,dirName2,
+                            JMXRemoteNamespaceMBean.class).connect();
+                    try {
+                        final ObjectName wombatName1 =
+                                new ObjectName("kanga//roo//"+conf.wombatName);
+                        final ObjectName wombatName2 =
+                                new ObjectName("kanga//roo//"+wombatName1);
+                        final WombatMBean wombat1 =
+                                JMX.newMBeanProxy(c,wombatName1,WombatMBean.class);
+                        final WombatMBean wombat2 =
+                                JMX.newMBeanProxy(c,wombatName2,WombatMBean.class);
+                        final String newCaption="I am still the same old wombat";
+                        wombat1.setCaption(newCaption);
+                        final String caps = conf.proxy.getCaption();
+                        System.out.println("Caption: "+caps);
+                        checkValue("Caption",newCaption,caps);
+                        final String caps1 = wombat1.getCaption();
+                        System.out.println("Caption #1: "+caps1);
+                        checkValue("Caption #1",newCaption,caps1);
+                        final String caps2 = wombat2.getCaption();
+                        System.out.println("Caption #2: "+caps2);
+                        checkValue("Caption #2",newCaption,caps2);
+                        final ObjectInstance instance =
+                                NamespaceController.createInstance(conf.server);
+                        final NamespaceControllerMBean controller =
+                                JMX.newMBeanProxy(conf.server,instance.getObjectName(),
+                                                  NamespaceControllerMBean.class);
+                        final String[] dirs = controller.findNamespaces();
+                        System.out.println("directories: " +
+                                Arrays.asList(dirs));
+                        final int depth = 4;
+                        final String[] dirs2 = controller.findNamespaces(null,null,depth);
+                        System.out.println("directories[depth="+depth+"]: " +
+                                Arrays.asList(dirs2));
+                        for (String dir : dirs2) {
+                            if (dir.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR))
+                                dir = dir.substring(0,dir.length()-
+                                        JMXNamespaces.NAMESPACE_SEPARATOR.length());
+                            if (dir.split(JMXNamespaces.NAMESPACE_SEPARATOR).length
+                                        > (depth+1)) {
+                                throw new RuntimeException(dir+": depth exceeds "+depth);
+                            }
+                            final ObjectName handlerName =
+                                    JMXNamespaces.getNamespaceObjectName(dir);
+                            final JMXNamespaceMBean handler =
+                                    JMX.newMBeanProxy(conf.server,handlerName,
+                                    JMXNamespaceMBean.class);
+                            try {
+                            System.err.println("Directory "+dir+" domains: "+
+                                    Arrays.asList(handler.getDomains()));
+                            System.err.println("Directory "+dir+" default domain: "+
+                                    handler.getDefaultDomain());
+                            System.err.println("Directory "+dir+" MBean count: "+
+                                    handler.getMBeanCount());
+                            } catch(Exception x) {
+                                System.err.println("get info failed for " +
+                                        dir +", "+handlerName+": "+x);
+                                x.getCause().printStackTrace();
+                                throw x;
+                            }
+                        }
+
+                    } finally {
+                        c.unregisterMBean(dirName2);
+                    }
+                } finally {
+                    c.unregisterMBean(dirName1);
+                    client.close();
+                    server.stop();
+                }
+            } finally {
+                conf.close();
+            }
+            System.err.println("recursiveTest PASSED");
+        } catch (Exception x) {
+            System.err.println("recursiveTest FAILED: " +x);
+            x.printStackTrace();
+            throw new RuntimeException(x);
+        }
+    }
+
+    /**
+     * Test cycle detection.
+     * mkdir test ; cd test ; ln -s . kanga ; ln -s kanga/kanga/roo/kanga roo
+     * touch kanga/roo/wombat
+     **/
+    public static void probeKangaRooTest(String[] args) {
+        final SimpleTestConf conf;
+        try {
+            conf = new SimpleTestConf(args);
+            try {
+                final JMXServiceURL url =
+                        new JMXServiceURL("rmi","localHost",0);
+                final Map<String,Object> empty = Collections.emptyMap();
+                final JMXConnectorServer server =
+                        JMXConnectorServerFactory.newJMXConnectorServer(url,
+                        empty,conf.server);
+                server.start();
+                final JMXServiceURL address = server.getAddress();
+                final JMXConnector client =
+                        JMXConnectorFactory.connect(address,
+                        empty);
+                final String[] signature = {
+                    JMXServiceURL.class.getName(),
+                    Map.class.getName(),
+                };
+
+                final Object[] params = {
+                    address,
+                    null,
+                };
+                final MBeanServerConnection c =
+                        client.getMBeanServerConnection();
+
+                // ln -s . kanga
+                final ObjectName dirName1 =
+                        new ObjectName("kanga//:type=JMXNamespace");
+                c.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                              dirName1, params,signature);
+                c.invoke(dirName1, "connect", null, null);
+                try {
+                    // ln -s kanga//kanga//roo//kanga roo
+                    final JMXNamespace local = new JMXNamespace(
+                            new MBeanServerConnectionWrapper(null,
+                            JMXNamespaceTest.class.getClassLoader()){
+
+                        @Override
+                        protected MBeanServerConnection getMBeanServerConnection() {
+                            return JMXNamespaces.narrowToNamespace(c,
+                                    "kanga//kanga//roo//kanga"
+                                    );
+                        }
+
+                    });
+                    final ObjectName dirName2 =
+                            new ObjectName("roo//:type=JMXNamespace");
+                    conf.server.registerMBean(local,dirName2);
+                    System.out.println(dirName2 + " created!");
+                    try {
+                        // touch kanga/roo/wombat
+                        final ObjectName wombatName1 =
+                                new ObjectName("kanga//roo//"+conf.wombatName);
+                        final WombatMBean wombat1 =
+                                JMX.newMBeanProxy(c,wombatName1,WombatMBean.class);
+                        final String newCaption="I am still the same old wombat";
+                        Exception x = null;
+                        try {
+                            wombat1.setCaption(newCaption);
+                        } catch (RuntimeOperationsException r) {
+                            x=r.getTargetException();
+                            System.out.println("Got expected exception: " + x);
+                            // r.printStackTrace();
+                        }
+                        if (x == null)
+                            throw new RuntimeException("cycle not detected!");
+                    } finally {
+                        c.unregisterMBean(dirName2);
+                    }
+                } finally {
+                    c.unregisterMBean(dirName1);
+                    client.close();
+                    server.stop();
+                }
+            } finally {
+                conf.close();
+            }
+            System.err.println("probeKangaRooTest PASSED");
+        } catch (Exception x) {
+            System.err.println("probeKangaRooTest FAILED: " +x);
+            x.printStackTrace();
+            throw new RuntimeException(x);
+        }
+    }
+    /**
+     * Test cycle detection 2.
+     * mkdir test ; cd test ; ln -s . roo ; ln -s roo/roo kanga
+     * touch kanga/roo/wombat ; rm roo ; ln -s kanga roo ;
+     * touch kanga/roo/wombat
+     *
+     **/
+    public static void probeKangaRooCycleTest(String[] args) {
+        final SimpleTestConf conf;
+        try {
+            conf = new SimpleTestConf(args);
+            Exception failed = null;
+            try {
+                final JMXServiceURL url =
+                        new JMXServiceURL("rmi","localHost",0);
+                final Map<String,Object> empty = Collections.emptyMap();
+                final JMXConnectorServer server =
+                        JMXConnectorServerFactory.newJMXConnectorServer(url,
+                        empty,conf.server);
+                server.start();
+                final JMXServiceURL address = server.getAddress();
+                final JMXConnector client =
+                        JMXConnectorFactory.connect(address,
+                        empty);
+                final String[] signature = {
+                    JMXServiceURL.class.getName(),
+                    Map.class.getName(),
+                };
+                final String[] signature2 = {
+                    JMXServiceURL.class.getName(),
+                    Map.class.getName(),
+                    String.class.getName()
+                };
+                final Object[] params = {
+                    address,
+                    Collections.emptyMap(),
+                };
+                final Object[] params2 = {
+                    address,
+                    null,
+                    "kanga",
+                };
+                final MBeanServerConnection c =
+                        client.getMBeanServerConnection();
+
+                // ln -s . roo
+                final ObjectName dirName1 =
+                        new ObjectName("roo//:type=JMXNamespace");
+                c.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                              dirName1, params,signature);
+                c.invoke(dirName1, "connect",null,null);
+                try {
+                    final Map<String,Object> emptyMap =
+                            Collections.emptyMap();
+                    final JMXNamespace local = new JMXNamespace(
+                            new MBeanServerConnectionWrapper(
+                            JMXNamespaces.narrowToNamespace(c,
+                            "roo//roo//"),
+                            JMXNamespaceTest.class.getClassLoader())) {
+                    };
+                    // ln -s roo/roo kanga
+                    final ObjectName dirName2 =
+                            new ObjectName("kanga//:type=JMXNamespace");
+                    conf.server.registerMBean(local,dirName2);
+                    System.out.println(dirName2 + " created!");
+                    try {
+                        // touch kanga/roo/wombat
+                        final ObjectName wombatName1 =
+                                new ObjectName("kanga//roo//"+conf.wombatName);
+                        final WombatMBean wombat1 =
+                                JMX.newMBeanProxy(c,wombatName1,WombatMBean.class);
+                        final String newCaption="I am still the same old wombat";
+                        wombat1.setCaption(newCaption);
+                        // rm roo
+                        c.unregisterMBean(dirName1);
+                        // ln -s kanga roo
+                        System.err.println("**** Creating " + dirName1 +
+                                " ****");
+                        c.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                              dirName1, params2,signature2);
+                        System.err.println("**** Created " + dirName1 +
+                                " ****");
+                        Exception x = null;
+                        try {
+                            // touch kanga/roo/wombat
+                            wombat1.setCaption(newCaption+" I hope");
+                        } catch (RuntimeOperationsException r) {
+                            x=(Exception)r.getCause();
+                            System.out.println("Got expected exception: " + x);
+                            //r.printStackTrace();
+                        }
+                        if (x == null)
+                            throw new RuntimeException("should have failed!");
+                        x = null;
+                        try {
+                            // ls kanga/roo/wombat
+                            System.err.println("**** Connecting " + dirName1 +
+                                    " ****");
+                            JMX.newMBeanProxy(c,dirName1,
+                                    JMXRemoteNamespaceMBean.class).connect();
+                            System.err.println("**** Connected " + dirName1 +
+                                    " ****");
+                        } catch (IOException r) {
+                            x=r;
+                            System.out.println("Got expected exception: " + x);
+                            //r.printStackTrace();
+                        }
+                        System.err.println("**** Expected Exception Not Raised ****");
+                        if (x == null) {
+                            System.out.println(dirName1+" contains: "+
+                                    c.queryNames(new ObjectName(
+                                    dirName1.getDomain()+"*:*"),null));
+                            throw new RuntimeException("cycle not detected!");
+                        }
+                    } catch (Exception t) {
+                        if (failed == null) failed = t;
+                    } finally {
+                            c.unregisterMBean(dirName2);
+                    }
+                } finally {
+                    try {
+                        c.unregisterMBean(dirName1);
+                    } catch (Exception t) {
+                        if (failed == null) failed = t;
+                        System.err.println("Failed to unregister "+dirName1+
+                                ": "+t);
+                    }
+                    try {
+                        client.close();
+                    } catch (Exception t) {
+                        if (failed == null) failed = t;
+                        System.err.println("Failed to close client: "+t);
+                    }
+                    try {
+                        server.stop();
+                    } catch (Exception t) {
+                        if (failed == null) failed = t;
+                        System.err.println("Failed to stop server: "+t);
+                    }
+                }
+            } finally {
+                try {
+                    conf.close();
+                } catch (Exception t) {
+                    if (failed == null) failed = t;
+                    System.err.println("Failed to stop server: "+t);
+                }
+            }
+            if (failed != null) throw failed;
+            System.err.println("probeKangaRooCycleTest PASSED");
+        } catch (Exception x) {
+            System.err.println("probeKangaRooCycleTest FAILED: " +x);
+            x.printStackTrace();
+            throw new RuntimeException(x);
+        }
+    }
+    public static void main(String[] args) {
+        simpleTest(args);
+        recursiveTest(args);
+        probeKangaRooTest(args);
+        probeKangaRooCycleTest(args);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXNamespaceViewTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,549 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test JMXNamespaceViewTest.java
+ * @summary Test the JMXNamespaceView class.
+ * @author Daniel Fuchs
+ * @run clean JMXNamespaceViewTest Wombat WombatMBean
+ * @run build JMXNamespaceViewTest Wombat WombatMBean
+ * @run main JMXNamespaceViewTest
+ */
+
+
+import java.lang.management.ManagementFactory;
+import java.net.ServerSocket;
+import java.rmi.registry.LocateRegistry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.management.JMException;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaceView;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXRemoteNamespace;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * A simple test to test the JMXNamespaceViewTest...
+ * @author dfuchs
+ */
+public class JMXNamespaceViewTest {
+
+    // TODO: Remove this when contexts are added.
+    public static class ClientContext {
+        public final static String NAMESPACE = "jmx.context";
+    }
+
+    /**
+     * Describe the configuration of a namespace
+     */
+    public static class NamespaceConfig {
+        /** name of the namespace - no // allowed **/
+        public String name;
+        /**
+         * JMXServiceURL through which the namespace is exported, if it
+         * is a remote namespace. {@code null} if the namespace is local.
+         * This is an inpur URL - eg: new JMXServiceURL("rmi",null,0).toString()
+         * is acceptable here.
+         */
+        public String jmxurl;
+        /**
+         * Values of the name= key for each WombatMBean contained in the
+         * namespace.
+         */
+        public String[] wombats;
+        /** list of child namespace **/
+        public NamespaceConfig[] children;
+    }
+
+    /**
+     * Creates a NamespaceConfig record for a local namespace.
+     * @param name     name  of the namespace
+     * @param wombats  names of WombatMBean it should contain.
+     * @return a NamespaceConfig.
+     */
+    public static NamespaceConfig config(String name, String[] wombats) {
+        return config(name,null,wombats);
+    }
+
+    /**
+     * Creates a NamespaceConfig record for a remote namespace.
+     * @param name    name  of the namespace
+     * @param jmxurl  input JMXServiceURL for creating the JMXConnectorServer
+     * @param wombats names of WombatMBean it should contain.
+     * @return a NamespaceConfig.
+     */
+    public static NamespaceConfig config(String name, String jmxurl,
+            String[] wombats) {
+        return config(name,jmxurl,wombats,(NamespaceConfig[])null);
+    }
+
+    /**
+     * Creates a NamespaceConfig record for a local namespace.
+     * @param name     name  of the namespace
+     * @param wombats  names of WombatMBean it should contain.
+     * @param children list  of sub namespaces.
+     * @return a NamespaceConfig.
+     */
+    public static NamespaceConfig config(String name, String[] wombats,
+            NamespaceConfig... children) {
+        return config(name,null,wombats,children);
+    }
+
+    /**
+     * Creates a NamespaceConfig record for a remote namespace.
+     * @param name    name  of the namespace
+     * @param jmxurl  input JMXServiceURL for creating the JMXConnectorServer
+     * @param wombats names of WombatMBean it should contain.
+     * @param children list  of sub namespaces.
+     * @return a NamespaceConfig.
+     */
+    static NamespaceConfig config(String name, String jmxurl, String[] wombats,
+            NamespaceConfig... children) {
+         final NamespaceConfig cfg = new NamespaceConfig();
+         cfg.name=name; cfg.jmxurl=jmxurl; cfg.wombats=wombats;
+         cfg.children=children;
+         return cfg;
+    }
+
+    /**
+     * Returns the given names. This is a utility method to ease code
+     * reading.
+     * @param names names of Wombat MBeans.
+     * @return the given names.
+     */
+    static String[] wombats(String... names) {
+        return names;
+    }
+
+    /**
+     * Creates a JMXServiceURL string for the given protocol.
+     * This is also a utility method to ease code reading.
+     * @param protocol The protocol name (e.g. "rmi")
+     * @return A JMXServiceURL string.
+     * @throws Exception if creation of the JMXServiceURL fails.
+     */
+    static String url(String protocol) throws Exception {
+        return new JMXServiceURL(protocol,null,0).toString();
+    }
+
+    /**
+     * Creates a config for a hierarchy of namespaces, mixing local namespaces
+     * and remote namespaces using the given protocol.
+     * @param protocol The protocol that should be used for remote namespaces.
+     * @return A namespace config hierarchy.
+     * @throws java.lang.Exception
+     */
+    public static NamespaceConfig[] makeConfig(String protocol)
+        throws Exception {
+        final NamespaceConfig[] config = {
+        // Top level namespace "top1" (local)
+        config("top1",wombats("wchief","w1","w2","w3"),
+                // top1//local1
+                config("local1",wombats("wchief","ww1","ww2")),
+                // top1//local2
+                config("local2",wombats("wchief","ww4","ww5","ww6"),
+                    // top1//local2//local3
+                    config("local3",wombats("wchief","www1","www2")),
+                    // top1//local2//rmi1
+                    config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))),
+                // top1//rmi2
+                config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"),
+                    // top1//rmi2//local4
+                    config("local4",wombats("wchief","www6","www7")),
+                    // top1//rmi2//rmi3
+                    config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"),
+                        // top1//rmi2//rmi3//local5
+                        config("local5",wombats("wchief","wwww1"))))),
+        // Top level namespace "top2" (local)
+        config("top2",wombats("wchief","w21","w22","w23"),
+                // top2//local21
+                config("local21",wombats("wchief","ww21","ww22")),
+                // top2//rmi22
+                config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"),
+                    // top2//rmi22//local24
+                    config("local24",wombats("wchief","www26","www27")),
+                    // top2//rmi22//rmi23
+                    config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"),
+                        // top2//rmi22//rmi23//local25
+                        config("local25",wombats("wchief","wwww21"))))),
+        // Top level namespace "top3" (remote)
+        config("top3",url(protocol),wombats("wchief","w31","w32","w33"),
+                // top3//local31
+                config("local31",wombats("wchief","ww31","ww32")),
+                // top3//rmi32
+                config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"),
+                    // top3//rmi32//local34
+                    config("local34",wombats("wchief","www36","www37")),
+                    // top3//rmi32//rmi33
+                    config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"),
+                        // top3//rmi32//local35
+                        config("local35",wombats("wchief","wwww31"))))),
+        };
+        return config;
+    }
+
+    /**
+     * Close all connector servers in the list.
+     * @param cslist List of connector servers to close.
+     */
+    public static void closeAll(List<JMXConnectorServer> cslist) {
+            for (JMXConnectorServer cs : cslist) {
+                try {
+                    cs.stop();
+                } catch (Exception xx) {
+                    System.err.println("Failed to stop connector: " + xx);
+                }
+            }
+    }
+
+    public static class MBeanServerConfigCreator {
+        public MBeanServer createMBeanServerFor(NamespaceConfig config) {
+            return MBeanServerFactory.newMBeanServer();
+        }
+    }
+
+    /**
+     * Load the given namespace configuration inside the given MBeanServer.
+     * Return a list of connector servers created in the process.
+     * @param server      The MBeanServer in which the namespaces must
+     *                    be created.
+     * @param namespaces  The list of namespaces to create.
+     * @return a list of started connector servers.
+     * @throws java.lang.Exception failed to create the specified namespaces.
+     */
+    public static List<JMXConnectorServer> load(MBeanServer server,
+           MBeanServerConfigCreator factory,
+           NamespaceConfig... namespaces) throws Exception {
+        final List<JMXConnectorServer> cslist =
+                new ArrayList<JMXConnectorServer>();
+        try {
+            final ObjectName creator =
+                    new ObjectName("jmx.creator:type=JMXNamespaceCreator");
+            if (System.getProperty("jmx.wait")!=null
+                    && !server.isRegistered(creator)) {
+                server.registerMBean(new JMXNamespaceCreator(),creator);
+            }
+            for (NamespaceConfig cfg : namespaces) {
+                final MBeanServer srv = factory.createMBeanServerFor(cfg);
+                if (System.getProperty("jmx.wait")!=null
+                    && !srv.isRegistered(creator)) {
+                    srv.registerMBean(new JMXNamespaceCreator(),creator);
+                }
+                if (cfg.wombats != null) {
+                    for (String w : cfg.wombats) {
+                        final ObjectName n =
+                                new ObjectName("wombat:type=Wombat,name=" + w);
+                        final WombatMBean ww = new Wombat();
+                        srv.registerMBean(ww, n);
+                    }
+                }
+                if (cfg.children != null) {
+                    cslist.addAll(load(srv, factory, cfg.children));
+                }
+                JMXNamespace nm;
+                if (cfg.jmxurl == null) {
+                    nm = new JMXNamespace(srv);
+                } else {
+                    JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL(cfg.jmxurl),
+                            null, srv);
+                    srv.registerMBean(cs,
+                            new ObjectName("jmx.remote:type=JMXConnectorServer"));
+                    cs.start();
+                    cslist.add(cs);
+                    nm = JMXRemoteNamespace.
+                            newJMXRemoteNamespace(cs.getAddress(),
+                            null);
+                }
+                server.registerMBean(nm,
+                        JMXNamespaces.getNamespaceObjectName(cfg.name));
+                if (nm instanceof JMXRemoteNamespace) {
+                    server.invoke(
+                            JMXNamespaces.getNamespaceObjectName(cfg.name),
+                            "connect", null, null);
+                }
+            }
+        } catch (Exception x) {
+            closeAll(cslist);
+            throw x;
+        }
+        return cslist;
+    }
+
+    /**
+     * Add an entry {@code <path,NamespaceConfig>} in the map for the given
+     * namespace and its subnamespaces.
+     * @param map    A {@code Map<path,NamespaceConfig>}.
+     * @param parent The path of the parent workspace.
+     * @param cfg    The NamespaceConfig hierarchy to index in the map.
+     */
+    public static void fillMap(Map<String,NamespaceConfig> map, String parent,
+            NamespaceConfig cfg) {
+
+        final String where;
+        if (parent == null || parent.equals(""))
+            where=cfg.name;
+        else
+            where=parent+JMXNamespaces.NAMESPACE_SEPARATOR+cfg.name;
+        map.put(where,cfg);
+        if (cfg.children==null) return;
+        for(NamespaceConfig child:cfg.children) {
+            fillMap(map,where,child);
+        }
+    }
+
+    /**
+     * Compare a list of namespace names obtained from JMXNamespaceView.list()
+     * with the expected clildren list of the corresponding NamespaceConfig.
+     * @param list      A list of namespace names
+     * @param children  A list of NamespaceConfig correspondng to expected
+     *                  namespace.
+     * @param fail      If true and the comparison yields false, throws an
+     *                  exception instead of simply returning false.
+     * @return true if OK, false if NOK.
+     */
+    private static boolean compare(String[] list, NamespaceConfig[] children,
+            boolean fail) {
+        final List<String> found = new ArrayList<String>(Arrays.asList(list));
+        if (found.contains(ClientContext.NAMESPACE))
+            found.remove(ClientContext.NAMESPACE);
+
+        if (children == null && found.size()==0) return  true;
+        if (children == null && fail == false) return false;
+        if (children == null) throw new RuntimeException(
+                "No child expected. Found "+Arrays.toString(list));
+        final Set<String> names = new HashSet<String>();
+        for (NamespaceConfig cfg : children) {
+            names.add(cfg.name);
+            if (found.contains(cfg.name)) continue;
+            if (!fail) return false;
+            throw new RuntimeException(cfg.name+" not found in "+
+                    found);
+        }
+        found.removeAll(names);
+        if (found.size()==0) return true;
+        if (fail==false) return false;
+        throw new RuntimeException("found additional namespaces: "+
+                found);
+    }
+
+    /**
+     * Compares the result of queryNames(null,null) with a set of expected
+     * wombats.
+     * @param where    The path of the namespace that was queried.
+     * @param list     The set of ObjectNames found.
+     * @param wombats  The expected list of wombats.
+     * @param fail      If true and the comparison yields false, throws an
+     *                  exception instead of simply returning false.
+     * @return true if OK, false if NOK.
+     * @throws java.lang.Exception something went wrong.
+     */
+    private static boolean compare(String where,
+            Set<ObjectName>list, String[] wombats,
+            boolean fail) throws Exception {
+        final Set<ObjectName> found = new HashSet<ObjectName>();
+        final Set<ObjectName> expected = new HashSet<ObjectName>();
+        for (ObjectName n : list) {
+            if ("Wombat".equals(n.getKeyProperty("type")))
+               found.add(n);
+        }
+        for(String w : wombats) {
+            final ObjectName n =
+                    new ObjectName("wombat:type=Wombat,name=" + w);
+            expected.add(n);
+            if (found.contains(n)) continue;
+            if (fail == false) return false;
+            throw new RuntimeException(where+
+                    ": Wombat "+w+" not found in "+found);
+        }
+        found.removeAll(expected);
+        if (found.size()==0) {
+            System.out.println(where+": found all expected: "+expected);
+            return true;
+        }
+        if (fail==false) return false;
+        throw new RuntimeException(where+": found additional MBeans: "+
+                found);
+    }
+
+    /**
+     * A generic test to test JMXNamespaceView over a namespace configuration.
+     * @param server      The MBeanServer in which to load the namespace
+     *                    config.
+     * @param namespaces  The namespace config to run the test over...
+     * @throws java.lang.Exception
+     */
+    public static void doTest(MBeanServer server, NamespaceConfig... namespaces)
+            throws Exception {
+        List<JMXConnectorServer> cslist = load(server,
+                new MBeanServerConfigCreator(), namespaces);
+        Map<String,NamespaceConfig> inputMap =
+                new HashMap<String,NamespaceConfig>();
+
+        for (NamespaceConfig cfg : namespaces) {
+            fillMap(inputMap,"",cfg);
+        }
+        try {
+            final JMXNamespaceView root = new JMXNamespaceView(server);
+            List<JMXNamespaceView> vlist = new ArrayList<JMXNamespaceView>();
+            vlist.add(root);
+
+            while (!vlist.isEmpty()) {
+                JMXNamespaceView v = vlist.remove(0);
+                final String where = v.isRoot()?"root":v.where();
+                System.out.println(where+": "+
+                   v.getMBeanServerConnection().queryNames(null,null));
+                for (String ns : v.list()) {
+                    final JMXNamespaceView down = v.down(ns);
+                    vlist.add(down);
+                    if (!down.where().equals(v.isRoot()?ns:where+
+                            JMXNamespaces.NAMESPACE_SEPARATOR+ns)) {
+                        throw new RuntimeException("path of "+down.where()+
+                            " should be "+(v.isRoot()?ns:where+
+                            JMXNamespaces.NAMESPACE_SEPARATOR+ns));
+                    }
+                    if (down.up().equals(v)) continue;
+                    throw new RuntimeException("parent of "+down.where()+
+                            " should be "+where);
+                }
+                final NamespaceConfig[] children;
+                final NamespaceConfig   cfg;
+                if (v.isRoot()) {
+                    children=namespaces;
+                    cfg = null;
+                } else {
+                    cfg = inputMap.get(where);
+                    children = cfg==null?null:cfg.children;
+                }
+                compare(v.list(),children,true);
+                if (!v.isRoot()) {
+                    if (where.endsWith(ClientContext.NAMESPACE)) {
+                        System.out.println(where+": skipping queryNames analysis");
+                        continue;
+                    }
+                    //System.out.println(where+": cfg is: "+cfg);
+                    compare(where,v.getMBeanServerConnection().
+                        queryNames(null, null),cfg.wombats,true);
+                }
+            }
+
+            exportAndWaitIfNeeded(server);
+        } finally {
+            closeAll(cslist);
+        }
+    }
+
+    public static interface JMXNamespaceCreatorMBean {
+        public ObjectInstance createLocalNamespace(String namespace)
+                throws JMException ;
+        public void removeLocalNamespace(String namespace)
+                throws JMException;
+    }
+
+    public static class JMXNamespaceCreator
+            implements MBeanRegistration,
+                      JMXNamespaceCreatorMBean {
+
+        private volatile MBeanServer mbeanServer;
+
+        public ObjectInstance createLocalNamespace(String namespace)
+            throws JMException {
+            return mbeanServer.registerMBean(
+                    new JMXNamespace(MBeanServerFactory.newMBeanServer()),
+                    JMXNamespaces.getNamespaceObjectName(namespace));
+        }
+
+        public void removeLocalNamespace(String namespace)
+            throws JMException {
+            mbeanServer.unregisterMBean(
+                    JMXNamespaces.getNamespaceObjectName(namespace));
+        }
+
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+                throws Exception {
+            mbeanServer = server;
+            return name;
+        }
+
+        public void postRegister(Boolean registrationDone) {
+        }
+
+        public void preDeregister() throws Exception {
+         }
+
+        public void postDeregister() {
+        }
+
+    }
+
+    public static void exportAndWaitIfNeeded(MBeanServer server)
+        throws Exception {
+                if (System.getProperty("jmx.wait")!=null) {
+                final int port = getPortFor("rmi");
+                LocateRegistry.createRegistry(port);
+                final JMXServiceURL url =
+                        new JMXServiceURL("rmi",null,port,
+                        "/jndi/rmi://localhost:"+port+"/jmxrmi");
+                final JMXConnectorServer cs =
+                        JMXConnectorServerFactory.
+                        newJMXConnectorServer(url, null, server);
+                cs.start();
+                try {
+                    System.out.println("RMI Server waiting at: "+cs.getAddress());
+                    System.in.read();
+                } finally {
+                    cs.stop();
+                }
+            }
+    }
+
+    public static int getPortFor(String protocol) throws Exception {
+        final int aport =
+              Integer.valueOf(System.getProperty("jmx."+protocol+".port","0"));
+        if (aport > 0) return aport;
+        final ServerSocket s = new ServerSocket(0);
+        try {
+            final int port = s.getLocalPort();
+            return port;
+        } finally {
+            s.close();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        doTest(ManagementFactory.getPlatformMBeanServer(),makeConfig("rmi"));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXNamespacesTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,647 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ * @test JMXNamespacesTest.java
+ * @summary Test the static method that rewrite ObjectNames in JMXNamespacesTest
+ * @author Daniel Fuchs
+ * @run clean JMXNamespacesTest
+ * @compile -XDignore.symbol.file=true JMXNamespacesTest.java
+ * @run main JMXNamespacesTest
+ */
+
+import com.sun.jmx.namespace.ObjectNameRouter;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.logging.Logger;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+
+/**
+ * Class JMXNamespacesTest
+ * @author Sun Microsystems, 2005 - All rights reserved.
+ */
+public class JMXNamespacesTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(JMXNamespacesTest.class.getName());
+
+    /** Creates a new instance of JMXNamespacesTest */
+    public JMXNamespacesTest() {
+    }
+
+    public static class CustomObject implements Serializable {
+        ObjectName toto;
+        String titi;
+        CustomObject(String toto, String titi) {
+            try {
+                this.toto = new ObjectName(toto);
+            } catch (MalformedObjectNameException m) {
+                throw new IllegalArgumentException(m);
+            }
+            this.titi = titi;
+        }
+        private Object[] data() {
+            return new Object[] {toto, titi};
+        }
+        @Override
+        public boolean equals(Object other) {
+            if (! (other instanceof CustomObject)) return false;
+            return Arrays.deepEquals(data(),((CustomObject)other).data());
+        }
+        @Override
+        public int hashCode() {
+            return Arrays.deepHashCode(data());
+        }
+    }
+
+    public static CustomObject obj(String toto, String titi) {
+        return new CustomObject(toto,titi);
+    }
+
+    private static String failure;
+
+    public static void testDeepRewrite() throws Exception {
+        failure = null;
+        String s1 = "x//y//d:k=v";
+        String s2 = "v//w//x//y//d:k=v";
+        String p1 = "v//w";
+        String p3 = "a//b";
+
+        System.out.println("inserting "+p1);
+        final CustomObject foo1 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s1,s1),"",p1);
+        assertEquals(foo1.toto.toString(),p1+"//"+s1);
+        assertEquals(foo1.titi,s1);
+
+       System.out.println("removing "+p1);
+       final CustomObject foo2 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,"");
+        assertEquals(foo2.toto.toString(),s1);
+        assertEquals(foo2.titi,s2);
+
+        System.out.println("removing "+p1);
+        final CustomObject foo3 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(p1+"//"+s2,s2),p1,"");
+        assertEquals(foo3.toto.toString(),s2);
+        assertEquals(foo3.titi,s2);
+
+        System.out.println("replacing "+p1+" with "+p3);
+        final CustomObject foo4 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,p3);
+        assertEquals(foo4.toto.toString(),p3+"//"+s1);
+        assertEquals(foo4.titi,s2);
+
+        System.out.println("replacing "+p1+" with "+p1);
+        final CustomObject foo5 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,p1);
+        assertEquals(foo5.toto.toString(),s2);
+        assertEquals(foo5.titi,s2);
+
+        System.out.println("removing x//y in "+s2);
+        try {
+            final CustomObject foo7 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),"x//y","");
+            failed("Remove x//y in "+s2+" should have failed!");
+        } catch (IllegalArgumentException x) {
+            System.out.println("Received expected exception: "+x);
+        }
+
+        System.out.println("replacing x//y with "+p3+" in "+s2);
+        try {
+            final CustomObject foo7 =
+                JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),"x//y",p3);
+            failed("Replace x//y in "+s2+" should have failed!");
+        } catch (IllegalArgumentException x) {
+            System.out.println("Received expected exception: "+x);
+        }
+
+        if (failure != null) throw new Exception(failure);
+    }
+
+    private static String[][] wildcards = {
+        { "", "*:*" },
+        { "//", "//*:*" },
+        { "foo", "foo//*:*" },
+        { "//foo", "//foo//*:*" },
+        { "////foo", "//foo//*:*" },
+        { "foo//", "foo//*:*" },
+        { "foo////", "foo//*:*" },
+        { "//foo//", "//foo//*:*" },
+        { "////foo//", "//foo//*:*" },
+        { "////foo////", "//foo//*:*" },
+        { "foo//bar", "foo//bar//*:*" },
+        { "//foo//bar", "//foo//bar//*:*" },
+        { "////foo//bar", "//foo//bar//*:*" },
+        { "foo//bar//", "foo//bar//*:*" },
+        { "foo//bar////", "foo//bar//*:*" },
+        { "//foo//bar//", "//foo//bar//*:*" },
+        { "////foo//bar//", "//foo//bar//*:*" },
+        { "////foo//bar////", "//foo//bar//*:*" },
+        { "foo////bar", "foo//bar//*:*" },
+        { "//foo////bar", "//foo//bar//*:*" },
+        { "////foo////bar", "//foo//bar//*:*" },
+        { "foo////bar//", "foo//bar//*:*" },
+        { "foo////bar////", "foo//bar//*:*" },
+        { "//foo////bar//", "//foo//bar//*:*" },
+        { "////foo////bar//", "//foo//bar//*:*" },
+        { "////foo////bar////", "//foo//bar//*:*" },
+        { "fo/o", "fo/o//*:*" },
+        { "//f/oo", "//f/oo//*:*" },
+        { "////f/o/o", "//f/o/o//*:*" },
+        { "fo/o//", "fo/o//*:*" },
+        { "f/oo////", "f/oo//*:*" },
+        { "//fo/o//", "//fo/o//*:*" },
+        { "////f/oo//", "//f/oo//*:*" },
+        { "////f/o/o////", "//f/o/o//*:*" },
+        { "foo//b/a/r", "foo//b/a/r//*:*" },
+        { "//fo/o//bar", "//fo/o//bar//*:*" },
+        { "////foo//b/ar", "//foo//b/ar//*:*" },
+        { "foo//ba/r//", "foo//ba/r//*:*" },
+        { "f/oo//bar////", "f/oo//bar//*:*" },
+        { "//f/o/o//bar//", "//f/o/o//bar//*:*" },
+        { "////foo//b/a/r//", "//foo//b/a/r//*:*" },
+        { "////f/o/o//b/a/r////", "//f/o/o//b/a/r//*:*" },
+        { "foo////ba/r", "foo//ba/r//*:*" },
+        { "//foo////b/ar", "//foo//b/ar//*:*" },
+        { "////f/oo////bar", "//f/oo//bar//*:*" },
+        { "fo/o////bar//", "fo/o//bar//*:*" },
+        { "foo////ba/r////", "foo//ba/r//*:*" },
+        { "//fo/o////ba/r//", "//fo/o//ba/r//*:*" },
+        { "////f/oo////b/ar//", "//f/oo//b/ar//*:*" },
+        { "////f/o/o////b/a/r////", "//f/o/o//b/a/r//*:*" },
+    };
+    private final static String[] badguys = {
+        null,
+        "/",         "/*:*",
+        "///",       "///*:*" ,
+        "/foo",      "/foo//*:*",
+        "//foo/",    "//foo///*:*" ,
+        "/////foo",  "///foo//*:*",
+        "/foo//",    "/foo//*:*",
+        "foo/////",  "foo///*:*",
+        "///foo//",  "///foo//*:*",
+        "////foo///", "//foo///*:*" ,
+        "/////foo/////", "///foo///*:*",
+        "/foo//bar", "/foo//bar//*:*",
+        "//foo///bar", "//foo///bar//*:*",
+        "/////foo////bar/", "///foo//bar///*:*",
+        "foo///bar//", "foo//bar///*:*",
+        "foo//bar/////", "foo///bar//*:*",
+        "///foo//bar//", "//foo///bar//*:*" ,
+    };
+    public static void testWildcard() throws Exception {
+        int i = 0;
+        for (String[] pair : wildcards) {
+            i++;
+            final String msg = "testWildcard[good,"+i+"] "+Arrays.asList(pair)+": ";
+            assertEquals(msg, new ObjectName(pair[1]),
+                    JMXNamespaces.getWildcardFor(pair[0]));
+        }
+        i=0;
+        for (String bad : badguys) {
+            i++;
+            try {
+                JMXNamespaces.getWildcardFor(bad);
+                failed("testWildcard[bad,"+i+"] "+bad+" incorrectly accepted. " +
+                        "IllegalArgumentException was expected");
+            } catch (IllegalArgumentException x) {
+                // OK
+            }
+        }
+        if (failure != null) throw new Exception(failure);
+    }
+
+    private static String[][] goodinsert = {
+        {"","d:k=v","d:k=v"},
+        {"","//d:k=v","//d:k=v"},
+        {"//","d:k=v","//d:k=v"},
+        {"//","//d:k=v","//d:k=v"},
+        {"//","a//d:k=v","//a//d:k=v"},
+        {"//","//a//d:k=v","//a//d:k=v"},
+        {"//","////a////d:k=v","//a//d:k=v"},
+        {"//b","////a////d:k=v","//b//a//d:k=v"},
+        {"b","////a////d:k=v","b//a//d:k=v"},
+        {"b","d:k=v","b//d:k=v"},
+        {"b//","d:k=v","b//d:k=v"},
+        {"//b//","d:k=v","//b//d:k=v"},
+        {"//b","////a////d:k=v","//b//a//d:k=v"},
+        {"b//c","////a////d:k=v","b//c//a//d:k=v"},
+        {"b//c","d:k=v","b//c//d:k=v"},
+        {"b//c//","d:k=v","b//c//d:k=v"},
+        {"//b//c//","d:k=v","//b//c//d:k=v"},
+        {"","/d:k=v","/d:k=v"},
+        {"","///d:k=v","///d:k=v"},
+        {"//","/d:k=v","///d:k=v"},
+        {"//","///d:k=v","///d:k=v"},
+        {"//","a///d:k=v","//a///d:k=v"},
+        {"//","//a///d:k=v","//a///d:k=v"},
+        {"//","////a////d/:k=v","//a//d/:k=v"},
+        {"//b","////a/////d:k=v","//b//a///d:k=v"},
+        {"b","////a////d/:k=v","b//a//d/:k=v"},
+        {"b","/d:k=v","b///d:k=v"},
+        {"b//","/d:k=v","b///d:k=v"},
+        {"//b//","/d:k=v","//b///d:k=v"},
+        {"//b","////a/////d:k=v","//b//a///d:k=v"},
+        {"b//c","////a/////d:k=v","b//c//a///d:k=v"},
+        {"b//c","/d:k=v","b//c///d:k=v"},
+        {"b//c//","/d:k=v","b//c///d:k=v"},
+        {"//b//c//","d/:k=v","//b//c//d/:k=v"},
+    };
+
+    private static String[][] badinsert = {
+        {"/","d:k=v"},
+        {"/","//d:k=v"},
+        {"///","d:k=v"},
+        {"///","//d:k=v"},
+        {"///","/a//d:k=v"},
+        {"///","///a//d:k=v"},
+        {"///","/////a////d:k=v"},
+        {"//b","/////a////d:k=v"},
+        {"b/","////a////d:k=v"},
+        {"b/","d:k=v"},
+        {"b///","d:k=v"},
+        {"//b///","d:k=v"},
+        {"//b/","////a////d:k=v"},
+        {"b///c","////a////d:k=v"},
+        {"b//c/","d:k=v"},
+        {"b///c//","d:k=v"},
+        {"//b///c//","d:k=v"},
+
+    };
+
+    public static void testInsertPath() throws Exception {
+        int i = 0;
+        for (String[] pair : goodinsert) {
+            i++;
+            final String msg = "testInsertPath[good,"+i+"] "+Arrays.asList(pair)+": ";
+            assertEquals(msg,new ObjectName(pair[2]),
+                    JMXNamespaces.insertPath(pair[0],
+                    new ObjectName(pair[1])));
+        }
+        i=0;
+        for (String[] bad : badinsert) {
+            i++;
+            try {
+                JMXNamespaces.insertPath(bad[0],
+                    new ObjectName(bad[1]));
+                failed("testInsertPath[bad,"+i+"] "+
+                        Arrays.asList(bad)+" incorrectly accepted. " +
+                        "IllegalArgumentException was expected");
+            } catch (IllegalArgumentException x) {
+                // OK
+            }
+        }
+        if (failure != null) throw new Exception(failure);
+    }
+
+    private static String[][] testpath  = {
+        {"/a/a/:k=v",""},
+        {"/:k=v",""},
+        {"bli:k=v",""},
+        {"///a/a/:k=v",""},
+        {"///:k=v",""},
+        {"//bli:k=v",""},
+        {"/////a/a/:k=v",""},
+        {"/////:k=v",""},
+        {"////bli:k=v",""},
+        {"y///a/a/:k=v","y"},
+        {"y///:k=v","y"},
+        {"y//bli:k=v","y"},
+        {"y/////a/a/:k=v","y"},
+        {"y/////:k=v","y"},
+        {"y////bli:k=v","y"},
+        {"//y///a/a/:k=v","y"},
+        {"//y///:k=v","y"},
+        {"//y//bli:k=v","y"},
+        {"//y/////a/a/:k=v","y"},
+        {"//y/////:k=v","y"},
+        {"//y////bli:k=v","y"},
+        {"////y///a/a/:k=v","y"},
+        {"////y///:k=v","y"},
+        {"////y//bli:k=v","y"},
+        {"////y/////a/a/:k=v","y"},
+        {"////y/////:k=v","y"},
+        {"////y////bli:k=v","y"},
+
+        {"z//y///a/a/:k=v","z//y"},
+        {"z//y///:k=v","z//y"},
+        {"z//y//bli:k=v","z//y"},
+        {"z//y/////a/a/:k=v","z//y"},
+        {"z//y/////:k=v","z//y"},
+        {"z//y////bli:k=v","z//y"},
+        {"//z//y///a/a/:k=v","z//y"},
+        {"//z//y///:k=v","z//y"},
+        {"//z//y//bli:k=v","z//y"},
+        {"//z//y/////a/a/:k=v","z//y"},
+        {"//z//y/////:k=v","z//y"},
+        {"//z//y////bli:k=v","z//y"},
+        {"z////y///a/a/:k=v","z//y"},
+        {"z////y///:k=v","z//y"},
+        {"z////y//bli:k=v","z//y"},
+        {"z////y/////a/a/:k=v","z//y"},
+        {"z////y/////:k=v","z//y"},
+        {"z////y////bli:k=v","z//y"},
+        {"//z////y///a/a/:k=v","z//y"},
+        {"//z////y///:k=v","z//y"},
+        {"//z////y//bli:k=v","z//y"},
+        {"//z////y/////a/a/:k=v","z//y"},
+        {"//z////y/////:k=v","z//y"},
+        {"//z////y////bli:k=v","z//y"},
+        {"////z////y///a/a/:k=v","z//y"},
+        {"////z////y///:k=v","z//y"},
+        {"////z////y//bli:k=v","z//y"},
+        {"////z////y/////a/a/:k=v","z//y"},
+        {"////z////y/////:k=v","z//y"},
+        {"////z////y////bli:k=v","z//y"},
+
+    };
+
+    public static void testGetNormalizedPath() throws Exception {
+        int i = 0;
+        for (String[] pair : testpath) {
+            i++;
+            final String msg = "testGetNormalizedPath["+i+"] "+Arrays.asList(pair)+": ";
+            assertEquals(msg,pair[1],
+                    JMXNamespaces.getContainingNamespace(new ObjectName(pair[0])));
+        }
+        if (failure != null) throw new Exception(failure);
+    }
+
+    private static String[][] testdomain  = {
+        {"/a/a/","/a/a/"},
+        {"/","/"},
+        {"bli","bli"},
+        {"///a/a/","///a/a/"},
+        {"///","///"},
+        {"//bli","//bli"},
+        {"/////a/a/","///a/a/"},
+        {"/////","///"},
+        {"////bli","//bli"},
+        {"y///a/a/","y///a/a/"},
+        {"y///","y///"},
+        {"y//bli","y//bli"},
+        {"y/////a/a/","y///a/a/"},
+        {"y/////","y///"},
+        {"y////bli","y//bli"},
+        {"//y///a/a/","//y///a/a/"},
+        {"//y///","//y///"},
+        {"//y//bli","//y//bli"},
+        {"//y/////a/a/","//y///a/a/"},
+        {"//y/////","//y///"},
+        {"//y////bli","//y//bli"},
+        {"////y///a/a/","//y///a/a/"},
+        {"////y///","//y///"},
+        {"////y//bli","//y//bli"},
+        {"////y/////a/a/","//y///a/a/"},
+        {"////y/////","//y///"},
+        {"////y////bli","//y//bli"},
+
+        {"z//y///a/a/","z//y///a/a/"},
+        {"z//y///","z//y///"},
+        {"z//y//bli","z//y//bli"},
+        {"z//y/////a/a/","z//y///a/a/"},
+        {"z//y/////","z//y///"},
+        {"z//y////bli","z//y//bli"},
+        {"//z//y///a/a/","//z//y///a/a/"},
+        {"//z//y///","//z//y///"},
+        {"//z//y//bli","//z//y//bli"},
+        {"//z//y/////a/a/","//z//y///a/a/"},
+        {"//z//y/////","//z//y///"},
+        {"//z//y////bli","//z//y//bli"},
+        {"z////y///a/a/","z//y///a/a/"},
+        {"z////y///","z//y///"},
+        {"z////y//bli","z//y//bli"},
+        {"z////y/////a/a/","z//y///a/a/"},
+        {"z////y/////","z//y///"},
+        {"z////y////bli","z//y//bli"},
+        {"//z////y///a/a/","//z//y///a/a/"},
+        {"//z////y///","//z//y///"},
+        {"//z////y//bli","//z//y//bli"},
+        {"//z////y/////a/a/","//z//y///a/a/"},
+        {"//z////y/////","//z//y///"},
+        {"//z////y////bli","//z//y//bli"},
+        {"////z////y///a/a/","//z//y///a/a/"},
+        {"////z////y///","//z//y///"},
+        {"////z////y//bli","//z//y//bli"},
+        {"////z////y/////a/a/","//z//y///a/a/"},
+        {"////z////y/////","//z//y///"},
+        {"////z////y////bli","//z//y//bli"},
+
+        {"bli//","bli//"},
+        {"//bli//","//bli//"},
+        {"////bli//","//bli//"},
+        {"y////","y//"},
+        {"y//bli//","y//bli//"},
+        {"y////","y//"},
+        {"y////bli//","y//bli//"},
+        {"//y////","//y//"},
+        {"//y//bli//","//y//bli//"},
+        {"//y//////","//y//"},
+        {"//y////bli//","//y//bli//"},
+        {"////y////","//y//"},
+        {"////y//bli////","//y//bli//"},
+        {"////y//////","//y//"},
+        {"////y////bli////","//y//bli//"},
+        {"z//y////","z//y//"},
+        {"z//y//bli//","z//y//bli//"},
+        {"z//y//////","z//y//"},
+        {"z//y////bli//","z//y//bli//"},
+        {"//z//y////","//z//y//"},
+        {"//z//y//bli//","//z//y//bli//"},
+        {"//z//y//////","//z//y//"},
+        {"//z//y////bli//","//z//y//bli//"},
+        {"z////y////","z//y//"},
+        {"z////y//bli//","z//y//bli//"},
+        {"z////y//////","z//y//"},
+        {"z////y////bli//","z//y//bli//"},
+        {"//z////y////","//z//y//"},
+        {"//z////y//bli//","//z//y//bli//"},
+        {"//z////y//////","//z//y//"},
+        {"//z////y////bli//","//z//y//bli//"},
+        {"////z////y////","//z//y//"},
+        {"////z////y//bli//","//z//y//bli//"},
+        {"////z////y//////","//z//y//"},
+        {"////z////y////bli//","//z//y//bli//"},
+
+    };
+    private static String[][] testnolead  = {
+        {"/a/a/","/a/a/"},
+        {"/","/"},
+        {"bli","bli"},
+        {"///a/a/","/a/a/"},
+        {"///","/"},
+        {"//bli","bli"},
+        {"/////a/a/","/a/a/"},
+        {"/////","/"},
+        {"////bli","bli"},
+        {"y///a/a/","y///a/a/"},
+        {"y///","y///"},
+        {"y//bli","y//bli"},
+        {"y/////a/a/","y///a/a/"},
+        {"y/////","y///"},
+        {"y////bli","y//bli"},
+        {"//y///a/a/","y///a/a/"},
+        {"//y///","y///"},
+        {"//y//bli","y//bli"},
+        {"//y/////a/a/","y///a/a/"},
+        {"//y/////","y///"},
+        {"//y////bli","y//bli"},
+        {"////y///a/a/","y///a/a/"},
+        {"////y///","y///"},
+        {"////y//bli","y//bli"},
+        {"////y/////a/a/","y///a/a/"},
+        {"////y/////","y///"},
+        {"////y////bli","y//bli"},
+
+        {"z//y///a/a/","z//y///a/a/"},
+        {"z//y///","z//y///"},
+        {"z//y//bli","z//y//bli"},
+        {"z//y/////a/a/","z//y///a/a/"},
+        {"z//y/////","z//y///"},
+        {"z//y////bli","z//y//bli"},
+        {"//z//y///a/a/","z//y///a/a/"},
+        {"//z//y///","z//y///"},
+        {"//z//y//bli","z//y//bli"},
+        {"//z//y/////a/a/","z//y///a/a/"},
+        {"//z//y/////","z//y///"},
+        {"//z//y////bli","z//y//bli"},
+        {"z////y///a/a/","z//y///a/a/"},
+        {"z////y///","z//y///"},
+        {"z////y//bli","z//y//bli"},
+        {"z////y/////a/a/","z//y///a/a/"},
+        {"z////y/////","z//y///"},
+        {"z////y////bli","z//y//bli"},
+        {"//z////y///a/a/","z//y///a/a/"},
+        {"//z////y///","z//y///"},
+        {"//z////y//bli","z//y//bli"},
+        {"//z////y/////a/a/","z//y///a/a/"},
+        {"//z////y/////","z//y///"},
+        {"//z////y////bli","z//y//bli"},
+        {"////z////y///a/a/","z//y///a/a/"},
+        {"////z////y///","z//y///"},
+        {"////z////y//bli","z//y//bli"},
+        {"////z////y/////a/a/","z//y///a/a/"},
+        {"////z////y/////","z//y///"},
+        {"////z////y////bli","z//y//bli"},
+
+        {"bli//","bli//"},
+        {"//bli//","bli//"},
+        {"////bli//","bli//"},
+        {"y////","y//"},
+        {"y//bli//","y//bli//"},
+        {"y////","y//"},
+        {"y////bli//","y//bli//"},
+        {"//y////","y//"},
+        {"//y//bli//","y//bli//"},
+        {"//y//////","y//"},
+        {"//y////bli//","y//bli//"},
+        {"////y////","y//"},
+        {"////y//bli////","y//bli//"},
+        {"////y//////","y//"},
+        {"////y////bli////","y//bli//"},
+        {"z//y////","z//y//"},
+        {"z//y//bli//","z//y//bli//"},
+        {"z//y//////","z//y//"},
+        {"z//y////bli//","z//y//bli//"},
+        {"//z//y////","z//y//"},
+        {"//z//y//bli//","z//y//bli//"},
+        {"//z//y//////","z//y//"},
+        {"//z//y////bli//","z//y//bli//"},
+        {"z////y////","z//y//"},
+        {"z////y//bli//","z//y//bli//"},
+        {"z////y//////","z//y//"},
+        {"z////y////bli//","z//y//bli//"},
+        {"//z////y////","z//y//"},
+        {"//z////y//bli//","z//y//bli//"},
+        {"//z////y//////","z//y//"},
+        {"//z////y////bli//","z//y//bli//"},
+        {"////z////y////","z//y//"},
+        {"////z////y//bli//","z//y//bli//"},
+        {"////z////y//////","z//y//"},
+        {"////z////y////bli//","z//y//bli//"},
+
+    };
+
+    public static void testNormalizeDomain() throws Exception {
+        int i = 0;
+        for (String[] pair : testdomain) {
+            i++;
+            final String msg = "testNormalizeDomain["+i+", false] "+Arrays.asList(pair)+": ";
+            assertEquals(msg,pair[1],
+                    ObjectNameRouter.normalizeDomain(pair[0],false));
+        }
+        if (failure != null) throw new Exception(failure);
+        i = 0;
+        for (String[] pair : testnolead) {
+            i++;
+            final String msg = "testNormalizeDomain["+i+", true] "+Arrays.asList(pair)+": ";
+            assertEquals(msg,pair[1],
+                    ObjectNameRouter.normalizeDomain(pair[0],true));
+        }
+        if (failure != null) throw new Exception(failure);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testDeepRewrite();
+        testNormalizeDomain();
+        testInsertPath();
+        testWildcard();
+        testGetNormalizedPath();
+    }
+
+    private static void assertEquals(Object x, Object y) {
+        assertEquals("",x,y);
+    }
+
+    private static void assertEquals(String msg, Object x, Object y) {
+        if (msg == null) msg="";
+        if (!equal(x, y))
+            failed(msg+"expected " + string(x) + "; got " + string(y));
+    }
+
+    private static boolean equal(Object x, Object y) {
+        if (x == y)
+            return true;
+        if (x == null || y == null)
+            return false;
+        if (x.getClass().isArray())
+            return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
+        return x.equals(y);
+    }
+
+    private static String string(Object x) {
+        String s = Arrays.deepToString(new Object[] {x});
+        return s.substring(1, s.length() - 1);
+    }
+
+
+    private static void failed(String why) {
+        failure = why;
+        new Throwable("FAILED: " + why).printStackTrace(System.out);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXRemoteNamespaceTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test JMXRemoteNamespaceTest.java
+ * @summary Basic tests on a JMXRemoteNamespace.
+ * @author Daniel Fuchs
+ * @run clean JMXRemoteNamespaceTest Wombat WombatMBean
+ * @run build JMXRemoteNamespaceTest Wombat WombatMBean
+ * @run main JMXRemoteNamespaceTest
+ */
+
+import javax.management.JMX;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotificationListener;
+import javax.management.namespace.JMXRemoteNamespace;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.io.IOException;
+import javax.management.AttributeChangeNotification;
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class JMXRemoteNamespaceTest {
+
+    static class MyConnect implements NotificationListener {
+        private final JMXRemoteNamespace my;
+        private final List<Notification> list;
+        private volatile int connectCount=0;
+        private int closeCount=0;
+        private final ObjectName myname;
+        public MyConnect(JMXRemoteNamespace my, ObjectName myname) {
+            this.my=my;
+            this.myname = myname;
+            list = Collections.synchronizedList(new ArrayList<Notification>());
+            my.addNotificationListener(this, null, null);
+        }
+
+        public synchronized void connect() throws IOException {
+                my.connect();
+                if (!my.isConnected())
+                    throw new IOException(myname+" should be connected");
+                connectCount++;
+        }
+
+        public void close() throws IOException {
+                my.close();
+                if (my.isConnected())
+                    throw new IOException(myname+" shouldn't be connected");
+                closeCount++;
+        }
+
+        public synchronized int getConnectCount() {
+            return connectCount;
+        }
+        public synchronized int getClosedCount() {
+            return closeCount;
+        }
+
+        public synchronized void handleNotification(Notification notification,
+                Object handback) {
+            list.add(notification);
+        }
+
+        public synchronized void checkNotifs(int externalConnect,
+                int externalClosed) throws Exception {
+            System.err.println("Connected: "+connectCount+" time"+
+                    ((connectCount>1)?"s":""));
+            System.err.println("Closed: "+closeCount+" time"+
+                    ((closeCount>1)?"s":""));
+            System.err.println("Received:");
+            int cl=0;
+            int co=0;
+            for (Notification n : list) {
+                System.err.println("\t"+n);
+                if (!(n instanceof AttributeChangeNotification))
+                    throw new Exception("Unexpected notif: "+n.getClass());
+                final AttributeChangeNotification acn =
+                        (AttributeChangeNotification)n;
+                if (((Boolean)acn.getNewValue()).booleanValue())
+                    co++;
+                else cl++;
+                if ((((Boolean)acn.getNewValue()).booleanValue())
+                    == (((Boolean)acn.getOldValue()).booleanValue())) {
+                    throw new Exception("Bad values: old=new");
+                }
+            }
+            if (! (list.size()==(closeCount+connectCount+
+                    externalClosed+externalConnect))) {
+                throw new Exception("Bad notif count - got "+list.size());
+            }
+            if (cl!=(closeCount+externalClosed)) {
+                throw new Exception("Bad count of close notif: expected "
+                        +(closeCount+externalClosed)+", got"+cl);
+            }
+            if (co!=(connectCount+externalConnect)) {
+                throw new Exception("Bad count of connect notif: expected "
+                        +(connectCount+externalConnect)+", got"+co);
+            }
+        }
+    }
+
+    public static void testConnectClose() throws Exception {
+        final MBeanServer myServer = MBeanServerFactory.newMBeanServer();
+        final JMXConnectorServer myRMI =
+                JMXConnectorServerFactory.newJMXConnectorServer(
+                new JMXServiceURL("rmi",null,0), null, myServer);
+        myRMI.start();
+        try {
+        final JMXRemoteNamespace my =
+                JMXRemoteNamespace.newJMXRemoteNamespace(
+                myRMI.getAddress(),null);
+        final MBeanServer s = MBeanServerFactory.newMBeanServer();
+        final ObjectName myname = JMXNamespaces.getNamespaceObjectName("my");
+        final ObjectName wname = ObjectName.getInstance("backyard:type=Wombat");
+        myServer.registerMBean(new Wombat(),wname);
+        final MyConnect myc = new MyConnect(my,myname);
+        myc.connect();
+        myc.close();
+        myc.connect();
+        s.registerMBean(my,myname);
+        myc.close();
+        myc.connect();
+        if (!s.queryNames(new ObjectName("my//b*:*"),null).contains(
+                JMXNamespaces.insertPath("my", wname))) {
+            throw new RuntimeException("1: Wombat not found: "+wname);
+        }
+        myc.close();
+        myc.connect();
+        final MBeanServer cd = JMXNamespaces.narrowToNamespace(s, "my");
+        if (!cd.queryNames(new ObjectName("b*:*"),null).contains(wname)) {
+            throw new RuntimeException("2: Wombat not found: "+wname);
+        }
+        myc.close();
+        myc.connect();
+        System.out.println("Found a Wombat in my backyard.");
+
+        final String deepThoughts = "I want to leave this backyard!";
+        final WombatMBean w = JMX.newMBeanProxy(cd, wname, WombatMBean.class);
+        w.setCaption(deepThoughts);
+        if (!deepThoughts.equals(w.getCaption()))
+                throw new RuntimeException("4: Wombat is not thinking right: "+
+                        w.getCaption());
+        s.unregisterMBean(myname);
+        if (my.isConnected())
+            throw new Exception(myname+" shouldn't be connected");
+        myc.connect();
+        myc.close();
+        myc.checkNotifs(0,1);
+        } finally {
+            myRMI.stop();
+        }
+
+    }
+
+    public static void main(String... args) throws Exception {
+        testConnectClose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/JMXRemoteTargetNamespace.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServerConnection;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.event.EventClient;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXRemoteNamespace;
+import javax.management.namespace.JMXRemoteNamespaceMBean;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+
+// These options originally in the draft of javax/management/namespaces
+// but we decided to retire them - since they could be implemented
+// by subclasses. The JMXRemoteTargetNamespace is such a subclass.
+//
+public class JMXRemoteTargetNamespace extends JMXRemoteNamespace {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(JMXRemoteTargetNamespace.class.getName());
+    public static final String CREATE_EVENT_CLIENT =
+           "jmx.test.create.event.client";
+
+    private final String sourceNamespace;
+    private final boolean createEventClient;
+
+    public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
+            Map<String,?> optionsMap) {
+        this(sourceURL,optionsMap,null);
+    }
+
+    public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
+            Map<String,?> optionsMap, String sourceNamespace) {
+        this(sourceURL,optionsMap,sourceNamespace,false);
+    }
+
+    public JMXRemoteTargetNamespace(JMXServiceURL sourceURL,
+            Map<String,?> optionsMap, String sourceNamespace,
+            boolean createEventClient) {
+        super(sourceURL,optionsMap);
+        this.sourceNamespace = sourceNamespace;
+        this.createEventClient = createEventClient(optionsMap);
+    }
+
+    private boolean createEventClient(Map<String,?> options) {
+        if (options == null) return false;
+        final Object createValue = options.get(CREATE_EVENT_CLIENT);
+        if (createValue == null) return false;
+        if (createValue instanceof Boolean)
+            return ((Boolean)createValue).booleanValue();
+        if (createValue instanceof String)
+            return Boolean.valueOf((String)createValue);
+        throw new IllegalArgumentException("Bad type for value of property " +
+                CREATE_EVENT_CLIENT+": "+createValue.getClass().getName());
+    }
+
+    @Override
+    protected JMXConnector newJMXConnector(JMXServiceURL url,
+            Map<String, ?> env) throws IOException {
+        JMXConnector sup = super.newJMXConnector(url, env);
+        if (sourceNamespace == null || "".equals(sourceNamespace))
+            return sup;
+        if (createEventClient)
+            sup = EventClient.withEventClient(sup);
+        return JMXNamespaces.narrowToNamespace(sup, sourceNamespace);
+    }
+
+
+    /**
+     * Creates a target name space to mirror a remote source name space in
+     * the target server.
+     * @param targetServer A connection to the target MBean server in which
+     *        the new name space should be created.
+     * @param targetPath the name space to create in the target server. Note
+     *        that if the target name space is a path - that is if
+     *        {@code targetPath} contains '//', then the parent name space
+     *        must be pre-existing in the target server. Attempting to create
+     *        {code targetPath="a//b//c"} in {@code targetServer}
+     *        will fail if name space {@code "a//b"} doesn't already exists
+     *        in {@code targetServer}.
+     * @param sourceURL a JMX service URL that can be used to connect to the
+     *        source MBean server.
+     * @param options the set of options to use when creating the
+     *        {@link #JMXRemoteNamespace JMXRemoteNamespace} that will
+     *        handle the new name space.
+     * @return An {@code ObjectInstance} representing the
+     *         {@link JMXRemoteNamespaceMBean} which handles the
+     *         new name space.
+     *
+     **/
+    public static ObjectInstance createNamespace(
+            MBeanServerConnection targetServer,
+            String targetPath,
+            JMXServiceURL sourceURL,
+            Map<String,?> options)
+        throws IOException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException {
+        final ObjectName name =
+                JMXNamespaces.getNamespaceObjectName(targetPath);
+        return createInstance(targetServer, name, sourceURL, options, null);
+    }
+
+    /**
+     * Creates a target name space to mirror a remote source name space in
+     * the target server.
+     * @param targetServer A connection to the target MBean server in which
+     *        the new name space should be created.
+     * @param targetPath the name space to create in the target server. Note
+     *        that if the target name space is a path - that is if
+     *        {@code targetPath} contains '//', then the parent name space
+     *        must be pre-existing in the target server. Attempting to create
+     *        {code targetPath="a//b//c"} in {@code targetServer}
+     *        will fail if name space {@code "a//b"} doesn't already exists
+     *        in {@code targetServer}.
+     * @param sourceURL a JMX service URL that can be used to connect to the
+     *        source MBean server.
+     * @param sourcePath the source namespace path insode the source server.
+     * @param options the set of options to use when creating the
+     *        {@link #JMXRemoteNamespace JMXRemoteNamespace} that will
+     *        handle the new name space.
+     * @return An {@code ObjectInstance} representing the
+     *         {@link JMXRemoteNamespaceMBean} which handles the
+     *         new name space.
+     *
+     **/
+    public static ObjectInstance createNamespace(
+            MBeanServerConnection targetServer,
+            String targetPath,
+            JMXServiceURL sourceURL,
+            Map<String,?> options,
+            String sourcePath)
+        throws IOException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException {
+        final ObjectName name =
+                JMXNamespaces.getNamespaceObjectName(targetPath);
+        return createInstance(targetServer, name, sourceURL, options, sourcePath);
+    }
+
+    /**
+     * Creates and registers a {@link JMXRemoteNamespaceMBean} in a target
+     * server, to mirror a remote source name space.
+     *
+     * @param server A connection to the target MBean server in which
+     *        the new name space should be created.
+     * @param handlerName the name of the JMXRemoteNamespace to create.
+     *        This must be a compliant name space handler name as returned
+     *        by {@link
+     *        JMXNamespaces#getNamespaceObjectName JMXNamespaces.getNamespaceObjectName}.
+     * @param sourceURL a JMX service URL that can be used to connect to the
+     *        source MBean server.
+     * @param sourcePath the path inside the source server
+     * @param options the set of options to use when creating the
+     *        {@link #JMXRemoteNamespace JMXRemoteNamespace} that will
+     *        handle the new name space.
+     * @return An {@code ObjectInstance} representing the new
+     *         {@link JMXRemoteNamespaceMBean} created.
+     * @see #createNamespace createNamespace
+     */
+     static ObjectInstance createInstance(MBeanServerConnection server,
+                  ObjectName handlerName,
+                  JMXServiceURL sourceURL, Map<String,?> options,
+                  String sourcePath)
+        throws IOException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException {
+        try {
+            final String[] signature = {
+                JMXServiceURL.class.getName(),
+                Map.class.getName(),
+                String.class.getName()
+            };
+            final Object[] params = {
+                sourceURL,options,sourcePath
+            };
+            final ObjectInstance instance =
+                server.createMBean(JMXRemoteTargetNamespace.class.getName(),
+                handlerName,params,signature);
+            return instance;
+        } catch (NotCompliantMBeanException ex) {
+            throw new RuntimeException("unexpected exception: " + ex, ex);
+        } catch (ReflectionException ex) {
+            throw new RuntimeException("unexpected exception: " + ex, ex);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/LazyDomainTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,789 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test LazyDomainTest.java
+ * @summary Basic test for Lazy Domains.
+ * @author Daniel Fuchs
+ * @run clean LazyDomainTest Wombat WombatMBean
+ * @run build LazyDomainTest Wombat WombatMBean
+ * @run main LazyDomainTest
+ */
+
+
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.Map;
+import java.util.Set;
+import javax.management.JMX;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerBuilder;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXDomain;
+import javax.management.remote.MBeanServerForwarder;
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class LazyDomainTest {
+    private static Map<String,Object> emptyEnvMap() {
+        return Collections.emptyMap();
+    }
+
+
+    public static interface MBeanServerLoader {
+        public MBeanServer loadMBeanServer();
+    }
+
+
+    public static class MBeanServerProxy implements InvocationHandler {
+
+        private final static Map<Method,Method> localMap;
+        static {
+            localMap = new HashMap<Method, Method>();
+            for (Method m : MBeanServerForwarder.class.getDeclaredMethods()) {
+                try {
+                    final Method loc = MBeanServerProxy.class.
+                            getMethod(m.getName(), m.getParameterTypes());
+                    localMap.put(m, loc);
+                } catch (Exception x) {
+                    // not defined...
+                }
+            }
+            try {
+                localMap.put(MBeanServer.class.
+                        getMethod("getMBeanCount", (Class[]) null),
+                        MBeanServerProxy.class.
+                        getMethod("getMBeanCount", (Class[]) null));
+            } catch (NoSuchMethodException x) {
+                // OK.
+            }
+        }
+
+        private final MBeanServerLoader loader;
+        private MBeanServer server;
+        private final Set<LazyDomain> domains;
+
+        public MBeanServerProxy(MBeanServerLoader loader) {
+            if (loader == null)
+                throw new IllegalArgumentException("null loader");
+            this.loader = loader;
+            this.server = null;
+            domains = new HashSet<LazyDomain>();
+        }
+
+
+        public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+            if (method.getDeclaringClass().equals(Object.class)) {
+                return invokeMethod(this,method,args);
+            }
+            final Method local = localMap.get(method);
+            if (local != null) {
+                return invokeMethod(this,local,args);
+            }
+            if (method.getDeclaringClass().equals(MBeanServer.class)) {
+                return invokeMethod(getMBeanServer(),method,args);
+            }
+            throw new NoSuchMethodException(method.getName());
+        }
+
+        private Object invokeMethod(Object on, Method method, Object[] args)
+            throws Throwable {
+            try {
+                return method.invoke(on, args);
+            } catch (InvocationTargetException ex) {
+                throw ex.getTargetException();
+            }
+        }
+
+        public synchronized MBeanServer getMBeanServer() {
+            if (server == null) setMBeanServer(loader.loadMBeanServer());
+            return server;
+        }
+
+        public synchronized void setMBeanServer(MBeanServer mbs) {
+            this.server = mbs;
+            if (mbs != null) {
+                for (LazyDomain dom : domains) dom.loaded();
+                domains.clear();
+            }
+        }
+
+        public synchronized boolean isLoaded() {
+            return server != null;
+        }
+
+        public synchronized void add(LazyDomain dom) {
+            if (isLoaded()) dom.loaded();
+            else domains.add(dom);
+        }
+
+        public synchronized boolean remove(LazyDomain dom) {
+            return domains.remove(dom);
+        }
+
+        public Integer getMBeanCount() {
+            if (isLoaded()) return server.getMBeanCount();
+            else return Integer.valueOf(0);
+        }
+    }
+
+    public static class LazyDomain extends JMXDomain {
+        public static MBeanServer makeProxyFor(MBeanServerProxy proxy) {
+            return (MBeanServer)
+                    Proxy.newProxyInstance(LazyDomain.class.getClassLoader(),
+                    new Class[] {MBeanServer.class, MBeanServerForwarder.class},
+                    proxy);
+        }
+
+        private final MBeanServerProxy proxy;
+        private volatile NotificationListener listener;
+        private volatile NotificationFilter   filter;
+
+        public LazyDomain(MBeanServerProxy proxy) {
+            super(makeProxyFor(proxy));
+            this.proxy = proxy;
+        }
+
+        @Override
+        public Integer getMBeanCount() {
+            if (proxy.isLoaded())
+                return super.getMBeanCount();
+            return 0;
+        }
+
+
+        @Override
+        public synchronized void addMBeanServerNotificationListener(
+                NotificationListener listener,
+                NotificationFilter filter) {
+            if (proxy.isLoaded()) {
+                super.addMBeanServerNotificationListener(listener, filter);
+            } else {
+                this.listener = listener;
+                this.filter   = filter;
+                proxy.add(this);
+            }
+        }
+
+        @Override
+        public synchronized void removeMBeanServerNotificationListener(
+                NotificationListener listener)
+                throws ListenerNotFoundException {
+            if (this.listener != listener)
+                throw new ListenerNotFoundException();
+            this.listener = null;
+            this.filter   = null;
+            if (proxy.isLoaded())
+                super.removeMBeanServerNotificationListener(listener);
+            proxy.remove(this);
+        }
+
+        public synchronized void loaded() {
+            if (listener != null)
+                addMBeanServerNotificationListener(listener, filter);
+        }
+
+    }
+
+    /**
+     * This is a use case for e.g GlassFish: the LazyStarterDomain MBean
+     * is a place holder that will unregister itself and autoload a set
+     * of MBeans in place of its own domain when that domain is
+     * accessed.
+     * This is an abstract class, where the only abstract method
+     * is loadMBeans(MBeanServer).
+     * Subclasses should implement that method to register whatever MBeans
+     * in the domain previously held by that LazyStarterDomain object.
+     * In other words: the LazyStarterDomain MBean is 'replaced' by the
+     * MBeans loaded by loadMBeans();
+     */
+    public static abstract class LazyStarterDomain extends LazyDomain {
+
+        /**
+         * This is a loader that will unregister the JMXDomain that
+         * created it, and register a bunch of MBeans in its place
+         * by calling LazyStarterDomain.loadMBeans
+         *
+         * That one gave me "la migraine".
+         */
+        private static class HalfGrainLoader implements MBeanServerLoader {
+            private volatile LazyStarterDomain domain;
+            public MBeanServer loadMBeanServer() {
+                if (domain == null)
+                    throw new IllegalStateException(
+                            "JMXDomain MBean not registered!");
+                final MBeanServer server     = domain.getMBeanServer();
+                final ObjectName  domainName = domain.getObjectName();
+                try {
+                    server.unregisterMBean(domainName);
+                } catch (Exception x) {
+                    throw new IllegalStateException("Can't unregister " +
+                            "JMXDomain: "+x,x);
+                }
+                domain.loadMBeans(server,domainName.getDomain());
+                return server;
+            }
+            public void setDomain(LazyStarterDomain domain) {
+                this.domain = domain;
+            }
+        }
+
+        /**
+         * This is an MBeanServerProxy which create a loader for the
+         * LazyStarterDomain MBean.
+         */
+        private static class DomainStarter extends MBeanServerProxy {
+
+            public DomainStarter() {
+                this(new HalfGrainLoader());
+            }
+
+            private final HalfGrainLoader loader;
+            private DomainStarter(HalfGrainLoader loader) {
+                super(loader);
+                this.loader = loader;
+            }
+
+            public void setDomain(LazyStarterDomain domain) {
+                loader.setDomain(domain);
+            }
+        }
+
+        /**
+         * A new LazyStarterDomain. When the domain monitored by this
+         * MBean is accessed, this MBean will unregister itself and call
+         * the abstract loadMBeans(MBeanServer) method.
+         * Subclasses need only to implement loadMBeans().
+         */
+        public LazyStarterDomain() {
+            this(new DomainStarter());
+        }
+
+        private LazyStarterDomain(DomainStarter starter) {
+            super(starter);
+            starter.setDomain(this);
+        }
+
+        // Contrarily to its LazyDomain superclass, this LazyDomain
+        // doesn't wrapp another MBeanServer: it simply registers a bunch
+        // of MBeans in its own MBeanServer.
+        // Thus, there's no notifications to forward.
+        //
+        @Override
+        public void addMBeanServerNotificationListener(
+                NotificationListener listener, NotificationFilter filter) {
+            // nothing to do.
+        }
+
+        // Contrarily to its LazyDomain superclass, this LazyDomain
+        // doesn't wrapp another MBeanServer: it simply registers a bunch
+        // of MBeans in its own MBeanServer.
+        // Thus, there's no notifications to forward.
+        //
+        @Override
+        public void removeMBeanServerNotificationListener(
+                NotificationListener listener) throws ListenerNotFoundException {
+            // nothing to do
+        }
+
+        // If this domain is registered, it contains no MBean.
+        // If it is not registered, then it no longer contain any MBean.
+        // The MBeanCount is thus always 0.
+        @Override
+        public Integer getMBeanCount() {
+            return 0;
+        }
+
+        /**
+         * Called when the domain is first accessed.
+         * {@code server} is the server in which this MBean was registered.
+         * A subclass must override this method in order to register
+         * the MBeans that should be contained in domain.
+         *
+         * @param server the server in which to load the MBeans.
+         * @param domain the domain in which the MBeans should be registered.
+         */
+        protected abstract void loadMBeans(MBeanServer server, String domain);
+
+
+    }
+
+    private static MBeanServerNotification pop(
+            BlockingQueue<Notification> queue,
+                                    String type,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        final Notification n = queue.poll(1, TimeUnit.SECONDS);
+        if (!(n instanceof MBeanServerNotification))
+            fail(test+"expected MBeanServerNotification, got "+n);
+        final MBeanServerNotification msn = (MBeanServerNotification)n;
+        if (!type.equals(msn.getType()))
+            fail(test+"expected "+type+", got "+msn.getType());
+        if (!mbean.apply(msn.getMBeanName()))
+            fail(test+"expected "+mbean+", got "+msn.getMBeanName());
+        System.out.println(test+" got: "+msn);
+        return msn;
+    }
+    private static MBeanServerNotification popADD(
+            BlockingQueue<Notification> queue,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        return pop(queue, MBeanServerNotification.REGISTRATION_NOTIFICATION,
+                mbean, test);
+    }
+
+    private static MBeanServerNotification popREM(
+            BlockingQueue<Notification> queue,
+                                    ObjectName mbean,
+                                    String test)
+                                    throws InterruptedException {
+        return pop(queue, MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
+                mbean, test);
+    }
+
+
+    private static void fail(String msg) {
+        raise(new RuntimeException(msg));
+    }
+
+    private static void fail(String msg, Throwable cause) {
+        raise(new RuntimeException(msg,cause));
+    }
+
+    private static void raise(RuntimeException x) {
+        lastException = x;
+        exceptionCount++;
+        throw x;
+    }
+
+    private static volatile Exception lastException = null;
+    private static volatile int       exceptionCount = 0;
+
+    // ZZZ need to add a test case with several LazyDomains, and
+    // need to test that nothing is loaded until the lazy domains
+    // are accessed...
+    //
+
+    private static void registerWombats(MBeanServer server, String domain,
+            int count) {
+        try {
+            for (int i=0;i<count;i++) {
+                final ObjectName name =
+                        new ObjectName(domain+":type=Wombat,name=wombat#"+i);
+                server.createMBean("Wombat", name);
+            }
+        } catch (RuntimeException x) {
+            throw x;
+        } catch(Exception x) {
+            throw new RuntimeException(x.toString(),x);
+        }
+    }
+
+    public static void checkSize(String test, MBeanServer server, int size) {
+        System.out.println("We have now "+server.getMBeanCount()+
+                " MBeans in "+Arrays.toString(server.getDomains()));
+        if (server.getMBeanCount() != size)
+            fail(test+"Expected "+size+
+                    " MBeans, found " + server.getMBeanCount());
+    }
+
+    private static MBeanServer newMBeanServer() {
+        return MBeanServerFactory.newMBeanServer();
+    }
+
+    public static void lazyTest() throws Exception {
+        final String test = "lazyTest: ";
+        System.out.println("" +
+                "\nThis test checks that it is possible to perform lazy loading" +
+                "\nof MBeans in a given domain by using a JMXDomain subclass" +
+                "\nfor that domain.");
+
+        System.out.println(test + " START");
+
+        // The "global" MBeanServer...
+        final MBeanServer server = newMBeanServer();
+
+        // An MBeanServer proxy which makes it possible to `lazy load'
+        // the platform MBeanServer domains inside the global MBeanServer.
+        //
+        final MBeanServerProxy  platform =
+                new MBeanServerProxy(new MBeanServerLoader() {
+
+            public MBeanServer loadMBeanServer() {
+                return ManagementFactory.getPlatformMBeanServer();
+            }
+        });
+
+
+        // The list of domain from the platform MBeanServer that will be
+        // lazily loaded in the global MBeanServer
+        //
+        final String[] platformDomains = {
+              "java.lang", "com.sun.management",
+              "java.util.logging", "java.nio"
+        };
+
+        // We create a second MBeanServer, in which we will store some
+        // custom MBeans. We will use this server to perform lazy loading
+        // of two domains: custom.awomb and custom.bwomb.
+        //
+        // We use an MBeanServerBuilder here so that the MBeans registered
+        // in our custom domain see all the MBeans in the global MBeanServer.
+        // We do this by saying that the 'outer' MBeanServer is our global
+        // servers. This means that the MBeans registered in the global
+        // MBeanServer will see the MBeans from custom.awomb and custom.bwomb,
+        // and the MBeans from custom.awomb and custom.bwomb will also see
+        // the MBeans from the global MBeanServer, including those from
+        // the platform domains.
+        //
+        final MBeanServerBuilder builder = new MBeanServerBuilder();
+        final MBeanServerDelegate delegate = builder.newMBeanServerDelegate();
+        final MBeanServer custom = builder.newMBeanServer("custom",
+                server, delegate);
+
+        // Number of MBean that we will put in each of the custom domain.
+        //
+        final int customCount = 10;
+
+        // We use one MBeanServer proxy for each of the custom domains.
+        // This makes it possible to load custom.awomb independently of
+        // custom.bwomb.
+        //
+        // Here, the logic of the loader is to register MBeans in the loaded
+        // domain as soon as the domain is loaded.
+        //
+        final MBeanServerProxy customa =
+                new MBeanServerProxy(new MBeanServerLoader() {
+            // A loader to register awomb MBeans in the custom MBeanServer.
+            public MBeanServer loadMBeanServer() {
+                registerWombats(custom, "custom.awomb", customCount);
+                return custom;
+            }
+        });
+        final MBeanServerProxy customb =
+                new MBeanServerProxy(new MBeanServerLoader() {
+            // A loader to register bwomb MBeans in the custom MBeanServer.
+            public MBeanServer loadMBeanServer() {
+                registerWombats(custom, "custom.bwomb", customCount);
+                return custom;
+            }
+        });
+
+        // A notification queue.
+        final BlockingQueue<Notification> queue =
+                new ArrayBlockingQueue<Notification>(100);
+
+        // A listener that puts notifs in the queue.
+        final NotificationListener l = new NotificationListener() {
+
+            public void handleNotification(Notification notification,
+                    Object handback) {
+                try {
+                    if (!queue.offer(notification, 5, TimeUnit.SECONDS)) {
+                        throw new RuntimeException("timeout exceeded");
+                    }
+                } catch (Exception x) {
+                    fail(test + "failed to handle notif", x);
+                }
+            }
+        };
+
+        // Create a LazyDomain for each of the platform domain.
+        // All platform domain share the same MBeanServer proxy, which means
+        // that loading one domain will also load all the others.
+        //
+        Map<String,LazyDomain> domainsMap = new HashMap<String,LazyDomain>();
+        for (String dom : platformDomains) {
+            domainsMap.put(dom, new LazyDomain(platform));
+        }
+        domainsMap.put("custom.awomb", new LazyDomain(customa));
+        domainsMap.put("custom.bwomb", new LazyDomain(customb));
+
+        for (Map.Entry<String,LazyDomain> e : domainsMap.entrySet()) {
+            server.registerMBean(e.getValue(),
+                    JMXDomain.getDomainObjectName(e.getKey()));
+        }
+
+        // check that lazy MBeans are not there...
+        checkSize(test,server,domainsMap.size()+1);
+
+        System.out.println(test+" registering listener with delegate.");
+        server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l,
+                null, null);
+
+        // check that lazy MBeans are not there...
+        checkSize(test,server,domainsMap.size()+1);
+
+        // force loading of custom.awomb.
+        final ObjectName awombat = new ObjectName(
+                "custom.awomb:type=Wombat,name=wombat#"+customCount/2);
+        if (!server.isRegistered(awombat))
+            fail(test+"Expected "+awombat+" to be reggistered!");
+
+        final int oldCount = domainsMap.size()+1+customCount;
+        checkSize(test,server,oldCount);
+
+        if (queue.peek() != null)
+            fail(test+"Received unexpected notifications: "+queue);
+
+
+        System.out.println(test+"creating a proxy for ClassLoadingMXBean.");
+        final ClassLoadingMXBean cl =
+                JMX.newMXBeanProxy(server,
+                new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME),
+                ClassLoadingMXBean.class);
+
+        checkSize(test,server,oldCount);
+
+        System.out.println(test+"Loaded classes: "+cl.getLoadedClassCount());
+
+        final int newCount = server.getMBeanCount();
+        if (newCount < oldCount+6)
+            fail(test+"Expected at least "+(oldCount+6)+
+                    " MBeans. Found "+newCount);
+
+        final ObjectName jwombat = new ObjectName("java.lang:type=Wombat");
+        server.createMBean("Wombat", jwombat);
+        System.out.println(test+"Created "+jwombat);
+        checkSize(test,server,newCount+1);
+
+        popADD(queue, jwombat, test);
+        if (queue.peek() != null)
+            fail(test+"Received unexpected notifications: "+queue);
+
+
+        int platcount = 0;
+        for (String dom : platformDomains) {
+            final Set<ObjectName> found =
+                    server.queryNames(new ObjectName(dom+":*"),null);
+            final int jcount = found.size();
+            System.out.println(test+"Found "+jcount+" MBeans in "+dom+
+                    ": "+found);
+            checkSize(test,server,newCount+1);
+            platcount += (jcount-1);
+        }
+        checkSize(test,server,oldCount+platcount);
+
+        final ObjectName owombat = new ObjectName("custom:type=Wombat");
+        server.createMBean("Wombat", owombat);
+        System.out.println(test+"Created "+owombat);
+        checkSize(test,server,newCount+2);
+        popADD(queue, owombat, test);
+        if (queue.peek() != null)
+            fail(test+"Received unexpected notifications: "+queue);
+
+        final Set<ObjectName> jwombatView = (Set<ObjectName>)
+                server.invoke(jwombat, "listMatching", new Object[] {null},
+                new String[] {ObjectName.class.getName()});
+        System.out.println(test+jwombat+" sees: "+jwombatView);
+        checkSize(test, server, newCount+2);
+        if (jwombatView.size() != (platcount+1))
+            fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" +
+                    " have seen "+(platcount+1));
+
+        final Set<ObjectName> platformMBeans =
+                ManagementFactory.getPlatformMBeanServer().
+                queryNames(null, null);
+        if (!platformMBeans.equals(jwombatView))
+            fail(test+jwombat+" should have seen "+platformMBeans);
+
+        // check that awombat triggers loading of bwombats
+        final Set<ObjectName> awombatView = (Set<ObjectName>)
+                server.invoke(awombat, "listMatching", new Object[] {null},
+                new String[] {ObjectName.class.getName()});
+        System.out.println(test+awombat+" sees: "+awombatView);
+        final int totalCount = newCount+2+customCount;
+        checkSize(test, server, totalCount);
+        if (awombatView.size() != totalCount)
+            fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" +
+                    " have seen "+totalCount);
+
+        final Set<ObjectName> allMBeans = server.
+                queryNames(null, null);
+        if (!allMBeans.equals(awombatView))
+            fail(test+awombat+" should have seen "+allMBeans);
+
+        System.out.println(test + " PASSED");
+
+    }
+
+
+    public static void lazyStarterTest() throws Exception {
+        final String test = "lazyStarterTest: ";
+        System.out.println("" +
+                "\nThis test checks that it is possible to perform lazy loading" +
+                "\nof MBeans in a given domain by using a transient JMXDomain" +
+                "\nsubclass for that domain. ");
+
+        System.out.println(test + " START");
+
+        // The "global" MBeanServer...
+        final MBeanServer platform =
+                ManagementFactory.getPlatformMBeanServer();
+
+        // A notification queue.
+        final BlockingQueue<Notification> queue =
+                new ArrayBlockingQueue<Notification>(100);
+
+        // A listener that puts notifs in the queue.
+        final NotificationListener l = new NotificationListener() {
+
+            public void handleNotification(Notification notification,
+                    Object handback) {
+                try {
+                    if (!queue.offer(notification, 5, TimeUnit.SECONDS)) {
+                        throw new RuntimeException("timeout exceeded");
+                    }
+                } catch (Exception x) {
+                    fail(test + "failed to handle notif", x);
+                }
+            }
+        };
+
+        System.out.println(test+" registering listener with delegate.");
+        platform.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l,
+                null, null);
+
+        final String ld1 = "lazy1";
+        final String ld2 = "lazy2";
+        final int wCount = 5;
+        final LazyStarterDomain lazy1 = new LazyStarterDomain() {
+            @Override
+            protected void loadMBeans(MBeanServer server, String domain) {
+                registerWombats(server, ld1, wCount);
+            }
+        };
+        final LazyStarterDomain lazy2 = new LazyStarterDomain() {
+            @Override
+            protected void loadMBeans(MBeanServer server, String domain) {
+                registerWombats(server, ld2, wCount);
+            }
+        };
+        final ObjectName lo1 = JMXDomain.getDomainObjectName(ld1);
+        final ObjectName lo2 = JMXDomain.getDomainObjectName(ld2);
+
+        final int initial = platform.getMBeanCount();
+
+        platform.registerMBean(lazy1, lo1);
+        System.out.println(test+"registered "+lo1);
+        checkSize(test, platform, initial+1);
+        popADD(queue, lo1, test);
+
+        platform.registerMBean(lazy2, lo2);
+        System.out.println(test+"registered "+lo2);
+        checkSize(test, platform, initial+2);
+        popADD(queue, lo2, test);
+
+
+        final ObjectName awombat = new ObjectName(
+                ld1+":type=Wombat,name=wombat#"+wCount/2);
+        if (!platform.isRegistered(awombat))
+            fail(test+"Expected "+awombat+" to be reggistered!");
+        checkSize(test,platform,initial+wCount+1);
+        popREM(queue, lo1, test);
+        final ObjectName pat1 =
+                new ObjectName(ld1+":type=Wombat,name=wombat#*");
+        for (int i=0;i<wCount;i++) {
+            popADD(queue,pat1,test);
+        }
+        System.out.println(test+"Found: "+
+                platform.queryNames(pat1,null));
+        checkSize(test,platform,initial+wCount+1);
+
+        final Set<ObjectName> all = platform.queryNames(null, null);
+        popREM(queue, lo2, test);
+        System.out.println(test+"Now found: "+all);
+        checkSize(test,platform,initial+wCount+wCount);
+        final ObjectName pat2 =
+                new ObjectName(ld2+":type=Wombat,name=wombat#*");
+        for (int i=0;i<wCount;i++) {
+            popADD(queue,pat2,test);
+        }
+
+        System.out.println(test+"check concurrent modification " +
+                "of the DomainDispatcher.");
+        System.out.println(test+"This will fail if the DomainDispatcher" +
+                " doesn't allow concurrent modifications.");
+        final HashMap<String,LazyStarterDomain> testConcurrent =
+                new HashMap<String,LazyStarterDomain>();
+        for (int i=0;i<(100/wCount);i++) {
+            final String ld = "concurrent.lazy"+i;
+            final LazyStarterDomain lazy = new LazyStarterDomain() {
+                @Override
+                protected void loadMBeans(MBeanServer server, String domain) {
+                    registerWombats(server, ld, wCount-1);
+                }
+            };
+            testConcurrent.put(ld, lazy);
+            final ObjectName lo = JMXDomain.getDomainObjectName(ld);
+            platform.registerMBean(lazy, lo);
+            popADD(queue, lo, test);
+        }
+
+        System.out.println(test+"Big autoload: "+
+                platform.queryNames(null,null));
+        System.out.println(test+"Big after load: "+
+                platform.queryNames(null,null));
+        if (!platform.queryNames(JMXDomain.getDomainObjectName("*"), null).
+                isEmpty()) {
+            fail(test+" some domains are still here: "+
+                    platform.queryNames(
+                    JMXDomain.getDomainObjectName("*"), null));
+        }
+        queue.clear();
+        System.out.println(test+"PASSED: The DomainDispatcher appears to be " +
+                "resilient to concurrent modifications.");
+    }
+
+    public static void main(String... args) throws Exception {
+
+        lazyTest();
+        lazyStarterTest();
+
+        if (lastException != null)
+            throw lastException;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/MXBeanRefTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2007-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.
+ *
+ * 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.
+ */
+/*
+ * @test MXBeanRefTest.java
+ * @bug 5072476
+ * @summary Test that MXBean proxy references work correctly in the presence
+ * of namespaces.
+ * @author Eamonn Mcmanus
+ */
+
+/**
+ * The idea is that we will create a hierarchy like this:
+ * a//
+ *   X
+ *   b//
+ *     Y
+ *     Z
+ * and we will use MXBean references so we have links like this:
+ * a//
+ *   X----+
+ *   b//  |
+ *       /
+ *     Y
+ *      \
+ *      /
+ *     Z
+ * In other words, X.getY() will return a proxy for Y, which the MXBean
+ * framework will map to b//Y.  A proxy for a//X should then map this
+ * into a proxy for a//b//Y.  That's easy.  But then if we call getZ()
+ * on this proxy, the MXBean framework will return just Z, and the proxy
+ * must map that into a proxy for a//b//Z.
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.openmbean.OpenDataException;
+
+public class MXBeanRefTest {
+
+    public static interface ZMXBean {
+        public void success();
+    }
+    public static class ZImpl implements ZMXBean {
+        public void success() {}
+    }
+
+    public static interface YMXBean {
+        public ZMXBean getZ();
+        public void setZ(ZMXBean z);
+    }
+    public static class YImpl implements YMXBean {
+        private ZMXBean z;
+
+        public YImpl(ZMXBean z) {
+            this.z = z;
+        }
+
+        public ZMXBean getZ() {
+            return z;
+        }
+
+        public void setZ(ZMXBean z) {
+            this.z = z;
+        }
+    }
+
+    public static interface XMXBean {
+        public YMXBean getY();
+    }
+    public static class XImpl implements XMXBean {
+        private final YMXBean yProxy;
+
+        public XImpl(YMXBean yProxy) {
+            this.yProxy = yProxy;
+        }
+
+        public YMXBean getY() {
+            return yProxy;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+        // Set up namespace hierarchy a//b//
+        MBeanServer ambs = MBeanServerFactory.newMBeanServer();
+        MBeanServer bmbs = MBeanServerFactory.newMBeanServer();
+        JMXNamespace bHandler = new JMXNamespace(bmbs);
+        ObjectName bHandlerName = JMXNamespaces.getNamespaceObjectName("b");
+        System.out.println(bHandlerName);
+        ambs.registerMBean(bHandler, bHandlerName);
+        JMXNamespace aHandler = new JMXNamespace(ambs);
+        ObjectName aHandlerName = JMXNamespaces.getNamespaceObjectName("a");
+        mbs.registerMBean(aHandler, aHandlerName);
+
+        ZMXBean z = new ZImpl();
+        ObjectName zName = new ObjectName("foo:type=Z");
+        bmbs.registerMBean(z, zName);
+
+        YMXBean y = new YImpl(z);
+        ObjectName yName = new ObjectName("foo:type=Y");
+        bmbs.registerMBean(y, yName);
+
+        ObjectName yNameInA = new ObjectName("b//" + yName);
+        System.out.println("MBeanInfo for Y as seen from a//:");
+        System.out.println(ambs.getMBeanInfo(yNameInA));
+        YMXBean yProxyInA = JMX.newMXBeanProxy(ambs, yNameInA, YMXBean.class);
+        XMXBean x = new XImpl(yProxyInA);
+        ObjectName xName = new ObjectName("foo:type=X");
+        ambs.registerMBean(x, xName);
+
+        ObjectName xNameFromTop = new ObjectName("a//" + xName);
+        XMXBean xProxy = JMX.newMXBeanProxy(mbs, xNameFromTop, XMXBean.class);
+        System.out.println("Name of X Proxy: " + proxyName(xProxy));
+        YMXBean yProxy = xProxy.getY();
+        System.out.println("Name of Y Proxy: " + proxyName(yProxy));
+        ZMXBean zProxy = yProxy.getZ();
+        System.out.println("Name of Z Proxy: " + proxyName(zProxy));
+
+        System.out.println("Operation through Z proxy...");
+        zProxy.success();
+
+        System.out.println("Changing Y's ref to Z...");
+        yProxy.setZ(zProxy);
+        zProxy = yProxy.getZ();
+        System.out.println("Name of Z Proxy now: " + proxyName(zProxy));
+        System.out.println("Operation through Z proxy again...");
+        zProxy.success();
+
+        System.out.println("Changing Y's ref to a bogus one...");
+        ZMXBean zProxyBad = JMX.newMXBeanProxy(mbs, zName, ZMXBean.class);
+        try {
+            yProxy.setZ(zProxyBad);
+        } catch (UndeclaredThrowableException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof OpenDataException) {
+                System.out.println("...correctly got UndeclaredThrowableException");
+                System.out.println("...wrapping: " + cause);
+            } else
+                throw new Exception("FAILED: wrong exception: " + cause);
+        }
+
+        System.out.println("Test passed");
+    }
+
+    private static ObjectName proxyName(Object proxy) {
+        InvocationHandler ih = Proxy.getInvocationHandler(proxy);
+        MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) ih;
+        return mbsih.getObjectName();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NamespaceController.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+
+import com.sun.jmx.namespace.ObjectNameRouter;
+import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMX;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXRemoteNamespaceMBean;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * The {@code NamespaceController} MBean makes it possible to easily
+ * create mount points ({@linkplain JMXNamespace JMXNamespaces}) in an
+ * {@code MBeanServer}.
+ * There is at most one instance of NamespaceController in an
+ * MBeanServer - which can be created using the {@link #createInstance
+ * createInstance} method. The {@code NamespaceController} MBean will
+ * make it possible to remotely create name spaces by mounting remote
+ * MBeanServers into the MBeanServer in which it was registered.
+ */
+// This API was originally in the draft of javax/management/namespaces
+// but we decided to retire it. Rather than removing all the associated
+// tests I have moved the API to the test hierarchy - so it is now used as
+// an additional (though somewhat complex) test case...
+//
+public class NamespaceController implements NamespaceControllerMBean,
+        NotificationEmitter, MBeanRegistration {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(NamespaceController.class.getName());
+
+    private static long seqNumber=0;
+
+    private final NotificationBroadcasterSupport broadcaster =
+            new NotificationBroadcasterSupport();
+
+    private volatile MBeanServer mbeanServer = null;
+
+    private volatile ObjectName objectName = null;
+
+    //was: NamespaceController.class.getPackage().getName()
+    public static final String NAMESPACE_CONTROLLER_DOMAIN = "jmx.ns";
+
+    /**
+     * Creates a new NamespaceController.
+     * Using {@link #createInstance} should be preferred.
+     **/
+    public NamespaceController() {
+        this(null);
+    }
+
+    public NamespaceController(MBeanServer mbeanServer) {
+        this.mbeanServer = mbeanServer;
+    }
+
+    /*
+     * MBeanNotification support
+     * You shouldn't update these methods
+     */
+    public final void addNotificationListener(NotificationListener listener,
+            NotificationFilter filter, Object handback) {
+        broadcaster.addNotificationListener(listener, filter, handback);
+    }
+
+    public MBeanNotificationInfo[] getNotificationInfo() {
+        return new MBeanNotificationInfo[] {
+        };
+    }
+
+    public final void removeNotificationListener(NotificationListener listener)
+        throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener);
+    }
+
+    public final void removeNotificationListener(NotificationListener listener,
+            NotificationFilter filter, Object handback)
+            throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener, filter, handback);
+    }
+
+    public static synchronized long getNextSeqNumber() {
+        return seqNumber++;
+    }
+
+    protected final void sendNotification(Notification n) {
+        if (n.getSequenceNumber()<=0)
+            n.setSequenceNumber(getNextSeqNumber());
+        if (n.getSource()==null)
+            n.setSource(objectName);
+        broadcaster.sendNotification(n);
+    }
+
+    /**
+     * The ObjectName with which this MBean was registered.
+     * <p>Unless changed by subclasses, this is
+     * {@code
+     *  "javax.management.namespace:type="+this.getClass().getSimpleName()}.
+     * @return this MBean's ObjectName, or null if this MBean was never
+     *         registered.
+     **/
+    public final ObjectName getObjectName() {
+        return objectName;
+    }
+
+    /**
+     * The MBeanServer  served by this NamespaceController.
+     * @return the MBeanServer  served by this NamespaceController.
+     **/
+    public final MBeanServer getMBeanServer() {
+        return mbeanServer;
+    }
+
+    /**
+     * Allows the MBean to perform any operations it needs before being
+     * registered in the MBean server. If the name of the MBean is not
+     * specified, the MBean can provide a name for its registration. If
+     * any exception is raised, the MBean will not be registered in the
+     * MBean server. Subclasses which override {@code preRegister}
+     * must call {@code super.preRegister(name,server)};
+     * @param server The MBean server in which the MBean will be registered.
+     * @param name The object name of the MBean.
+     *        The name must be either {@code null} - or equal to that
+     *        described by {@link #getObjectName}.
+     * @return The name under which the MBean is to be registered.
+     *         This will be the name described by {@link #getObjectName}.
+     * @throws MalformedObjectNameException if the supplied name does not
+     *        meet expected requirements.
+     */
+    public ObjectName preRegister(MBeanServer server, ObjectName name)
+        throws MalformedObjectNameException {
+        objectName = name;
+        final ObjectName single =
+                ObjectName.getInstance(NAMESPACE_CONTROLLER_DOMAIN+
+                ":type="+this.getClass().getSimpleName());
+        if (name!=null && !single.equals(name))
+            throw new MalformedObjectNameException(name.toString());
+        if (mbeanServer == null) mbeanServer = server;
+        return single;
+    }
+
+    /**
+     * Allows the MBean to perform any operations needed after having
+     * been registered in the MBean server or after the registration has
+     * failed.
+     * @param registrationDone Indicates whether or not the MBean has been
+     * successfully registered in the MBean server. The value false means
+     * that the registration has failed.
+     */
+    public void postRegister(Boolean registrationDone) {
+        //TODO postRegister implementation;
+    }
+
+    /**
+     * Allows the MBean to perform any operations it needs before being
+     * unregistered by the MBean server.
+     * @throws Exception This exception will be caught by the MBean server and
+     * re-thrown as an MBeanRegistrationException.
+     */
+    public void preDeregister() throws Exception {
+        //TODO preDeregister implementation;
+    }
+
+    /**
+     * Allows the MBean to perform any operations needed after having been
+     * unregistered in the MBean server.
+     */
+    public void postDeregister() {
+        //TODO postDeregister implementation;
+    }
+
+    public String mount(JMXServiceURL url,
+            String targetPath,
+            Map<String,Object> optionsMap)
+            throws IOException {
+        return mount(url, targetPath, "", optionsMap);
+    }
+
+    // see NamespaceControllerMBean
+    public String mount(JMXServiceURL url,
+            String targetPath,
+            String sourcePath,
+            Map<String,Object> optionsMap)
+            throws IOException {
+
+        // TODO: handle description.
+        final String dirName =
+                JMXNamespaces.normalizeNamespaceName(targetPath);
+
+         try {
+            final ObjectInstance moi =
+                    JMXRemoteTargetNamespace.createNamespace(mbeanServer,
+                    dirName,url,optionsMap,
+                    JMXNamespaces.normalizeNamespaceName(sourcePath)
+                    );
+            final ObjectName nsMBean = moi.getObjectName();
+            try {
+                mbeanServer.invoke(nsMBean, "connect", null,null);
+            } catch (Throwable t) {
+                mbeanServer.unregisterMBean(nsMBean);
+                throw t;
+            }
+            return getMountPointID(nsMBean);
+        } catch (InstanceAlreadyExistsException x) {
+            throw new IllegalArgumentException(targetPath,x);
+         } catch (IOException x) {
+            throw x;
+        } catch (Throwable x) {
+            if (x instanceof Error) throw (Error)x;
+            Throwable cause = x.getCause();
+            if (cause instanceof IOException)
+                throw ((IOException)cause);
+            if (cause == null) cause = x;
+
+            final IOException io =
+                    new IOException("connect failed: "+cause);
+            io.initCause(cause);
+            throw io;
+        }
+    }
+
+    private String getMountPointID(ObjectName dirName) {
+            return dirName.toString();
+    }
+
+    private ObjectName getHandlerName(String mountPointID) {
+        try {
+            final ObjectName tryit = ObjectName.getInstance(mountPointID);
+            final ObjectName formatted =
+                    JMXNamespaces.getNamespaceObjectName(tryit.getDomain());
+            if (!formatted.equals(tryit))
+                throw new IllegalArgumentException(mountPointID+
+                        ": invalid mountPointID");
+            return formatted;
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(mountPointID,x);
+        }
+    }
+
+    public boolean unmount(String mountPointID)
+        throws IOException {
+        final ObjectName dirName = getHandlerName(mountPointID);
+        if (!mbeanServer.isRegistered(dirName))
+            throw new IllegalArgumentException(mountPointID+
+                    ": no such name space");
+        final JMXRemoteNamespaceMBean mbean =
+                JMX.newMBeanProxy(mbeanServer,dirName,
+                    JMXRemoteNamespaceMBean.class);
+        try {
+            mbean.close();
+        } catch (IOException io) {
+            LOG.fine("Failed to close properly - ignoring exception: "+io);
+            LOG.log(Level.FINEST,
+                    "Failed to close properly - ignoring exception",io);
+        } finally {
+            try {
+                mbeanServer.unregisterMBean(dirName);
+            } catch (InstanceNotFoundException x) {
+                throw new IllegalArgumentException(mountPointID+
+                        ": no such name space", x);
+            } catch (MBeanRegistrationException x) {
+                final IOException io =
+                        new IOException(mountPointID +": failed to unmount");
+                io.initCause(x);
+                throw io;
+            }
+        }
+        return true;
+    }
+
+    public boolean ismounted(String targetPath) {
+        return mbeanServer.isRegistered(JMXNamespaces.getNamespaceObjectName(targetPath));
+    }
+
+    public ObjectName getHandlerNameFor(String targetPath) {
+        return JMXNamespaces.getNamespaceObjectName(targetPath);
+    }
+
+    public String[] findNamespaces() {
+        return findNamespaces(null,null,0);
+    }
+
+
+    private ObjectName getDirPattern(String from) {
+        try {
+            if (from == null)
+                return ObjectName.getInstance(ALL_NAMESPACES);
+            final String namespace =
+                  ObjectNameRouter.normalizeNamespacePath(from,false,true,false);
+            if (namespace.equals(""))
+                return ObjectName.getInstance(ALL_NAMESPACES);
+            if (JMXNamespaces.getNamespaceObjectName(namespace).isDomainPattern())
+                throw new IllegalArgumentException(from);
+            return ObjectName.getInstance(namespace+NAMESPACE_SEPARATOR+ALL_NAMESPACES);
+        } catch (MalformedObjectNameException x) {
+            throw new IllegalArgumentException(from,x);
+        }
+    }
+
+    public String[] findNamespaces(String from, String regex, int depth) {
+        if (depth < 0) return new String[0];
+        final Set<String> res = new TreeSet<String>();
+        final ObjectName all = getDirPattern(from);
+        Set<ObjectName> names = mbeanServer.queryNames(all,null);
+        for (ObjectName dirName : names) {
+            final String dir = dirName.getDomain();
+            if (regex == null || dir.matches(regex))
+                res.add(dir);
+            if (depth > 0)
+                res.addAll(Arrays.asList(findNamespaces(dir,regex,depth-1)));
+        }
+        return res.toArray(new String[res.size()]);
+    }
+
+    /**
+     * Creates a {@link NamespaceController} MBean in the provided
+     * {@link MBeanServerConnection}.
+     * <p>The name of the MBean is that returned by {@link #preRegister}
+     * as described by {@link #getObjectName}.
+     * @throws IOException if an {@code IOException} is raised when invoking
+     *         the provided connection.
+     * @throws InstanceAlreadyExistsException if an MBean was already
+     *         registered with the NamespaceController's name.
+     * @throws MBeanRegistrationException if thrown by {@link
+     * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
+     * server.createMBean}
+     * @throws MBeanException if thrown by {@link
+     * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
+     * server.createMBean}
+     * @return the {@link ObjectInstance}, as returned by {@link
+     * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName)
+     * server.createMBean}
+     **/
+    public static ObjectInstance createInstance(MBeanServerConnection server)
+        throws IOException, InstanceAlreadyExistsException,
+            MBeanRegistrationException, MBeanException {
+        try {
+            final ObjectInstance instance =
+                server.createMBean(NamespaceController.class.getName(), null);
+            return instance;
+        } catch (NotCompliantMBeanException ex) {
+            throw new RuntimeException("unexpected exception: " + ex, ex);
+        } catch (ReflectionException ex) {
+            throw new RuntimeException("unexpected exception: " + ex, ex);
+        }
+    }
+
+    private final static String ALL_NAMESPACES=
+            "*"+NAMESPACE_SEPARATOR+":"+
+            JMXNamespace.TYPE_ASSIGNMENT;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NamespaceControllerMBean.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.management.ObjectName;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * The {@link NamespaceController} MBean makes it possible to easily
+ * create mount points ({@link JMXNamespace JMXNamespaces}) in an
+ * {@code MBeanServer}.
+ */
+// This API was originally in the draft of javax/management/namespaces
+// but we decided to retire it. Rather than removing all the associated
+// tests I have moved the API to the test hierarchy - so it is now used as
+// an additional (though somewhat complex) test case...
+//
+public interface NamespaceControllerMBean {
+    /**
+     * Mount MBeans from the source path of the source URL into the specified
+     * target path of the target.
+     * @param url URL of the mounted source.
+     * @param targetPath Target path in which MBeans will be mounted.
+     * @param optionsMap connection map and options. See {@link
+     *      javax.management.namespace.JMXRemoteNamespace.Options
+     *      JMXRemoteNamespace.Options}
+     * @throws IOException Connection with the source failed
+     * @throws IllegalArgumentException Supplied parameters are
+     *         illegal, or combination of supplied parameters is illegal.
+     * @return A mount point id.
+     */
+    public String mount(JMXServiceURL url,
+            String targetPath,
+            Map<String,Object> optionsMap)
+            throws IOException, IllegalArgumentException;
+
+    /**
+     * Mount MBeans from the source path of the source URL into the specified
+     * target path of the target.
+     * @param url URL of the mounted source.
+     * @param targetPath Target path in which MBeans will be mounted.
+     * @param sourcePath source namespace path.
+     * @param optionsMap connection map and options. See {@link
+     *      javax.management.namespace.JMXRemoteNamespace.Options
+     *      JMXRemoteNamespace.Options}
+     * @throws IOException Connection with the source failed
+     * @throws IllegalArgumentException Supplied parameters are
+     *         illegal, or combination of supplied parameters is illegal.
+     * @return A mount point id.
+     */
+    public String mount(JMXServiceURL url,
+            String targetPath,
+            String sourcePath,
+            Map<String,Object> optionsMap)
+            throws IOException, IllegalArgumentException;
+
+    /**
+     * Unmount a previously mounted mount point.
+     * @param mountPointId A mount point id, as previously returned
+     *        by mount.
+     * @throws IllegalArgumentException Supplied parameters are
+     *         illegal, or combination of supplied parameters is illegal.
+     * @throws IOException thrown if the mount point {@link JMXNamespace}
+     *         couldn't be unregistered.
+     */
+    public boolean unmount(String mountPointId)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Tells whether there already exists a {@link JMXNamespace} for
+     * the given <var>targetPath</var>.
+     * @param targetPath a target name space path.
+     * @return true if a {@link JMXNamespace} is registered for that
+     *         name space path.
+     **/
+    public boolean ismounted(String targetPath);
+
+    /**
+     * Returns the handler name for the provided target name space
+     * path. Can throw IllegalArgumentException if the provided
+     * targetPath contains invalid characters (like e.g. ':').
+     * @param targetPath A target name space path.
+     * @return the handler name for the provided target name space
+     * path.
+     **/
+    public ObjectName getHandlerNameFor(String targetPath);
+
+    /**
+     * Return a sorted array of locally mounted name spaces.
+     * This is equivalent to calling {@link
+     * #findNamespaces(java.lang.String,java.lang.String,int)
+     *  findNamespaces(null,null,0)};
+     * @return a sorted array of locally mounted name spaces.
+     **/
+    public String[] findNamespaces();
+
+    /**
+     * Return a sorted array of mounted name spaces, starting at
+     * <var>from</var> (if non null), and recursively searching up to
+     * provided <var>depth</var>.
+     * @param from A name spaces from which to start the search. If null,
+     *        will start searching from the MBeanServer root.
+     *        If not null, all returned names will start with <var>from//</var>.
+     * @param regex A regular expression that the returned names must match.
+     *        If null - no matching is performed and all found names are
+     *        returned. If not null, then all returned names satisfy
+     *        {@link String#matches name.matches(regex)};
+     * @param depth the maximum number of levels that the search algorithm
+     *        will cross. 0 includes only top level name spaces, 1 top level
+     *        and first level children etc... <var>depth</var> is evaluated
+     *        with regard to where the search starts - if a non null
+     *        <var>from</var> parameter is provided - then {@code depth=0}
+     *        corresponds to all name spaces found right below
+     *        <var>from//</var>.
+     * @return A sorted array of name spaces matching the provided criteria.
+     *         All returned names end with "//".
+     **/
+    public String[] findNamespaces(String from, String regex, int depth);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NamespaceCreationTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test NamespaceCreationTest.java
+ * @summary General JMXNamespace test.
+ * @author Daniel Fuchs
+ * @run clean NamespaceCreationTest Wombat WombatMBean
+ * @run build NamespaceCreationTest Wombat WombatMBean
+ * @run main NamespaceCreationTest
+ */
+
+
+import java.util.Collections;
+import java.util.Map;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.RuntimeMBeanException;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+
+/**
+ * Test simple creation/registration of namespace.
+ *
+ */
+public class NamespaceCreationTest {
+    private static Map<String,Object> emptyEnvMap() {
+        return Collections.emptyMap();
+    }
+
+
+    public static class LocalNamespace extends JMXNamespace {
+
+        public LocalNamespace() {
+            super(MBeanServerFactory.newMBeanServer());
+        }
+
+    }
+
+    private static MBeanServer newMBeanServer() {
+        return MBeanServerFactory.newMBeanServer();
+    }
+
+    public static interface ThingMBean {}
+    public static class Thing implements ThingMBean, MBeanRegistration {
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+                throws Exception {
+            if (name == null) return new ObjectName(":type=Thing");
+            else return name;
+        }
+        public void postRegister(Boolean registrationDone) {
+        }
+
+        public void preDeregister() throws Exception {
+        }
+        public void postDeregister() {
+        }
+    }
+
+    /**
+     * Test that it is possible to create a dummy MBean with a null
+     * ObjectName - this is just a sanity check - as there are already
+     * other JMX tests that check that.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testCreateWithNull() throws Exception {
+        final MBeanServer server = newMBeanServer();
+        final ObjectInstance oi = server.registerMBean(new Thing(),null);
+        server.unregisterMBean(oi.getObjectName());
+        System.out.println("testCreateWithNull PASSED");
+    }
+
+    /**
+     * Check that we can register a JMXNamespace MBean, using its standard
+     * ObjectName.
+     * @throws java.lang.Exception
+     */
+    public static void testGoodObjectName() throws Exception {
+        MBeanServer server = newMBeanServer();
+        final ObjectName name =
+                JMXNamespaces.getNamespaceObjectName("gloups");
+        final ObjectInstance oi =
+                server.registerMBean(new LocalNamespace(),name);
+        System.out.println("Succesfully registered namespace: "+name);
+        try {
+            if (! name.equals(oi.getObjectName()))
+                throw new RuntimeException("testGoodObjectName: TEST failed: " +
+                        "namespace registered as: "+
+                    oi.getObjectName()+" expected: "+name);
+        } finally {
+            server.unregisterMBean(oi.getObjectName());
+        }
+        System.out.println("Succesfully unregistered namespace: "+name);
+        System.out.println("testGoodObjectName PASSED");
+    }
+
+    /**
+     * Check that we cannot register a JMXNamespace MBean, if we don't use
+     * its standard ObjectName.
+     * @throws java.lang.Exception
+     */
+    public static void testBadObjectName() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name = new ObjectName("d:k=v");
+        try {
+            server.registerMBean(new LocalNamespace(),name);
+            System.out.println("testBadObjectName: " +
+                    "Error: MBean registered, no exception thrown.");
+        } catch(RuntimeMBeanException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected RuntimeMBeanException - got "+
+                    x);
+        }
+        if (exp == null)  server.unregisterMBean(name);
+        if (exp == null)
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadObjectName: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadObjectName PASSED");
+    }
+
+    /**
+     * Check that we cannot register a Wombat MBean in a namespace that does
+     * not exists.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testBadNamespace() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name = new ObjectName("glips//d:k=v");
+
+        try {
+            server.registerMBean(new Wombat(),name);
+            System.out.println("testBadNamespace: " +
+                    "Error: MBean registered, no exception thrown.");
+        } catch(MBeanRegistrationException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadNamespace: TEST failed: " +
+                    "expected MBeanRegistrationException - got "+
+                    x);
+        }
+        if (exp == null)  server.unregisterMBean(name);
+        if (exp == null)
+            throw new RuntimeException("testBadNamespace: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadNamespace: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadNamespace PASSED");
+    }
+
+    /**
+     * Check that we cannot register a Wombat MBean with a domain name
+     * that ends with //. This is reserved for namespaces.
+     *
+     * @throws java.lang.Exception
+     */
+    public static void testBadDomain() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name = new ObjectName("glups//:k=v");
+
+        try {
+            server.registerMBean(new Wombat(),name);
+            System.out.println("testBadDomain: Error: MBean registered, no exception thrown.");
+        } catch(RuntimeMBeanException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected RuntimeMBeanException - got "+
+                    x);
+        }
+        if (exp == null)  server.unregisterMBean(name);
+        if (exp == null)
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadDomain: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadDomain PASSED");
+    }
+
+    /**
+     * Check that we cannot register a Wombat MBean as if it were a
+     * JMXNamespace. Only JMXNamespace MBeans can have JMX Namespace names.
+     * @throws java.lang.Exception
+     */
+    public static void testBadClassName() throws Exception {
+        MBeanServer server = newMBeanServer();
+        Throwable exp = null;
+        final ObjectName name =
+                JMXNamespaces.getNamespaceObjectName("glops");
+        try {
+            server.registerMBean(new Wombat(),name);
+            System.out.println("testBadClassName: " +
+                    "Error: MBean registered, no exception thrown.");
+        } catch(RuntimeMBeanException x) {
+            exp = x.getCause();
+        } catch(Exception x) {
+            throw new RuntimeException("testBadClassName: TEST failed: " +
+                    "expected RuntimeMBeanException - got "+
+                    x);
+        }
+        if (exp == null)  server.unregisterMBean(name);
+        if (exp == null)
+            throw new RuntimeException("testBadClassName: TEST failed: " +
+                    "expected IllegalArgumentException - got none");
+        if (!(exp instanceof IllegalArgumentException))
+            throw new RuntimeException("testBadClassName: TEST failed: " +
+                    "expected IllegalArgumentException - got "+
+                    exp.toString(),exp);
+        System.out.println("Got expected exception: "+exp);
+        System.out.println("testBadClassName PASSED");
+    }
+
+    public static void main(String... args) throws Exception {
+        testCreateWithNull();
+        testGoodObjectName();
+        testBadObjectName();
+        testBadNamespace();
+        testBadDomain();
+        testBadClassName();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NamespaceNotificationsTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test NamespaceNotificationsTest.java 1.12
+ * @summary General Namespace & Notifications test.
+ * @author Daniel Fuchs
+ * @run clean NamespaceNotificationsTest
+ *            Wombat WombatMBean JMXRemoteTargetNamespace
+ *            NamespaceController NamespaceControllerMBean
+ * @compile -XDignore.symbol.file=true  NamespaceNotificationsTest.java
+ *            Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java
+ *            NamespaceController.java NamespaceControllerMBean.java
+ * @run main NamespaceNotificationsTest
+ */
+import com.sun.jmx.remote.util.EventClientConnection;
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanServerNotification;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.loading.MLet;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXAddressable;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class NamespaceNotificationsTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(NamespaceNotificationsTest.class.getName());
+
+    /** Creates a new instance of NamespaceNotificationsTest */
+    public NamespaceNotificationsTest() {
+    }
+
+
+    public static JMXServiceURL export(MBeanServer server)
+    throws Exception {
+        final JMXServiceURL in = new JMXServiceURL("rmi",null,0);
+        final JMXConnectorServer cs =
+                JMXConnectorServerFactory.newJMXConnectorServer(in,null,null);
+        final ObjectName csname = ObjectName.
+                getInstance(cs.getClass().getPackage().getName()+
+                ":type="+cs.getClass().getSimpleName());
+        server.registerMBean(cs,csname);
+        cs.start();
+        return cs.getAddress();
+    }
+
+    public static class Counter {
+        int count;
+        public synchronized int count() {
+            count++;
+            notifyAll();
+            return count;
+        }
+        public synchronized int peek() {
+            return count;
+        }
+        public synchronized int waitfor(int max, long timeout)
+        throws InterruptedException {
+            final long start = System.currentTimeMillis();
+            while (count < max && timeout > 0) {
+                final long rest = timeout -
+                        (System.currentTimeMillis() - start);
+                if (rest <= 0) break;
+                wait(rest);
+            }
+            return count;
+        }
+    }
+
+    public static class CounterListener
+            implements NotificationListener {
+        final private Counter counter;
+        public CounterListener(Counter counter) {
+            this.counter = counter;
+        }
+        public void handleNotification(Notification notification,
+                Object handback) {
+            System.out.println("Received notif from " + handback +
+                    ":\n\t" + notification);
+            if (!notification.getSource().equals(handback)) {
+                System.err.println("OhOh... Unexpected source: \n\t"+
+                        notification.getSource()+"\n\twas expecting:\n\t"+
+                        handback);
+            }
+            counter.count();
+        }
+    }
+
+    public static void simpleTest(String[] args) {
+        try {
+            final MBeanServer server1 =
+                    ManagementFactory.getPlatformMBeanServer();
+            final JMXServiceURL url1 = export(server1);
+
+            final MBeanServer server2 =
+                    MBeanServerFactory.createMBeanServer("server2");
+            final JMXServiceURL url2 = export(server2);
+
+            final MBeanServer server3 =
+                    MBeanServerFactory.createMBeanServer("server3");
+            final JMXServiceURL url3 = export(server3);
+
+            final ObjectInstance ncinst =
+                    NamespaceController.createInstance(server1);
+
+            final NamespaceControllerMBean nc =
+                    JMX.newMBeanProxy(server1,ncinst.getObjectName(),
+                    NamespaceControllerMBean.class);
+
+            final Map<String,Object> options = new HashMap<String,Object>();
+            options.put(JMXRemoteTargetNamespace.CREATE_EVENT_CLIENT,"true");
+
+            final String mount1 =
+                    nc.mount(url1,"server1",options);
+            final String mount2 = nc.mount(url2,"server1//server2",
+                    options);
+            final String mount3 = nc.mount(url3,
+                    "server1//server2//server3",
+                    options);
+            final String mount13 = nc.mount(
+                    url1,
+                    "server3",
+                    "server2//server3",
+                    options);
+            final String mount21 = nc.mount(url1,"server2//server1",
+                    options);
+            final String mount31 = nc.mount(
+                    url1,
+                    "server3//server1",
+                    "server1",
+                    options);
+            final String mount32 = nc.mount(
+                    url1,
+                    "server3//server2",
+                    "server2",
+                    options);
+
+
+            final ObjectName deep =
+                    new ObjectName("server1//server2//server3//bush:type=Wombat,name=kanga");
+            server1.createMBean(Wombat.class.getName(),deep);
+
+            System.err.println("There's a wombat in the bush!");
+
+            final Counter counter = new Counter();
+
+            final NotificationListener listener =
+                    new CounterListener(counter);
+
+            final JMXConnector jc = JMXConnectorFactory.connect(url1);
+            final MBeanServerConnection aconn =
+                    EventClientConnection.getEventConnectionFor(
+                        jc.getMBeanServerConnection(),null);
+            aconn.addNotificationListener(deep,listener,null,deep);
+
+
+            final JMXServiceURL urlx = new JMXServiceURL(url1.toString());
+            System.out.println("conn: "+urlx);
+            final JMXConnector jc2 = JMXNamespaces.narrowToNamespace(
+                    JMXConnectorFactory.connect(urlx),"server1//server1");
+            final JMXConnector jc3 = JMXNamespaces.narrowToNamespace(jc2,"server3");
+            jc3.connect();
+            System.out.println("JC#3: " +
+                    ((jc3 instanceof JMXAddressable)?
+                        ((JMXAddressable)jc3).getAddress():
+                        jc3.toString()));
+            final MBeanServerConnection bconn =
+                    jc3.getMBeanServerConnection();
+            final ObjectName shallow =
+                    new ObjectName("bush:"+
+                    deep.getKeyPropertyListString());
+            final WombatMBean proxy =
+                    JMX.newMBeanProxy(EventClientConnection.getEventConnectionFor(
+                        bconn,null),shallow,WombatMBean.class,true);
+
+            ((NotificationEmitter)proxy).
+                    addNotificationListener(listener,null,shallow);
+            proxy.setCaption("I am a new Wombat!");
+            System.err.println("New caption: "+proxy.getCaption());
+            final int rcvcount = counter.waitfor(2,3000);
+            if (rcvcount != 2)
+                throw new RuntimeException("simpleTest failed: "+
+                        "received count is " +rcvcount);
+
+            System.err.println("simpleTest passed: got "+rcvcount+
+                    " notifs");
+
+        } catch (RuntimeException x) {
+            throw x;
+        } catch (Exception x) {
+            throw new RuntimeException("simpleTest failed: " + x,x);
+        }
+    }
+
+    public static class LocalNamespace extends
+            JMXNamespace {
+        LocalNamespace() {
+            super(MBeanServerFactory.newMBeanServer());
+        }
+
+    }
+
+    public static class ContextObject<K,V> {
+        public final K name;
+        public final V object;
+        public ContextObject(K name, V object) {
+            this.name = name;
+            this.object = object;
+        }
+        private Object[] data() {
+            return new Object[] {name,object};
+        }
+
+        @Override
+        public boolean equals(Object x) {
+            if (x instanceof ContextObject)
+                return Arrays.deepEquals(data(),((ContextObject<?,?>)x).data());
+            return false;
+        }
+        @Override
+        public int hashCode() {
+            return Arrays.deepHashCode(data());
+        }
+    }
+
+    private static <K,V> ContextObject<K,V> context(K k, V v) {
+        return new ContextObject<K,V>(k,v);
+    }
+
+    private static ObjectName name(String name) {
+        try {
+            return new ObjectName(name);
+        } catch(MalformedObjectNameException x) {
+            throw new IllegalArgumentException(name,x);
+        }
+    }
+
+    public static void simpleTest2() {
+        try {
+            System.out.println("\nsimpleTest2: STARTING\n");
+            final LocalNamespace foo = new LocalNamespace();
+            final LocalNamespace joe = new LocalNamespace();
+            final LocalNamespace bar = new LocalNamespace();
+            final MBeanServer server = MBeanServerFactory.newMBeanServer();
+
+            server.registerMBean(foo,JMXNamespaces.getNamespaceObjectName("foo"));
+            server.registerMBean(joe,JMXNamespaces.getNamespaceObjectName("foo//joe"));
+            server.registerMBean(bar,JMXNamespaces.getNamespaceObjectName("foo//bar"));
+            final BlockingQueue<ContextObject<String,MBeanServerNotification>> queue =
+                    new ArrayBlockingQueue<ContextObject<String,MBeanServerNotification>>(20);
+
+            final NotificationListener listener = new NotificationListener() {
+                public void handleNotification(Notification n, Object handback) {
+                    if (!(n instanceof MBeanServerNotification)) {
+                        System.err.println("Error: expected MBeanServerNotification");
+                        return;
+                    }
+                    final MBeanServerNotification mbsn =
+                            (MBeanServerNotification) n;
+
+                    // We will pass the namespace name in the handback.
+                    //
+                    final String namespace = (String) handback;
+                    System.out.println("Received " + mbsn.getType() +
+                            " for MBean " + mbsn.getMBeanName() +
+                            " from name space " + namespace);
+                    try {
+                        queue.offer(context(namespace,mbsn),500,TimeUnit.MILLISECONDS);
+                    } catch (Exception x) {
+                        System.err.println("Failed to enqueue received notif: "+mbsn);
+                        x.printStackTrace();
+                    }
+                }
+            };
+
+            server.addNotificationListener(JMXNamespaces.insertPath("foo//joe",
+                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//joe");
+            server.addNotificationListener(JMXNamespaces.insertPath("foo//bar",
+                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//bar");
+            server.createMBean(MLet.class.getName(),
+                    name("foo//joe//domain:type=MLet"));
+            checkQueue(queue,"foo//joe",
+                    MBeanServerNotification.REGISTRATION_NOTIFICATION);
+            server.createMBean(MLet.class.getName(),
+                    name("foo//bar//domain:type=MLet"));
+            checkQueue(queue,"foo//bar",
+                    MBeanServerNotification.REGISTRATION_NOTIFICATION);
+            server.unregisterMBean(
+                    name("foo//joe//domain:type=MLet"));
+            checkQueue(queue,"foo//joe",
+                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+            server.unregisterMBean(
+                    name("foo//bar//domain:type=MLet"));
+            checkQueue(queue,"foo//bar",
+                    MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+        } catch (RuntimeException x) {
+            System.err.println("FAILED: "+x);
+            throw x;
+        } catch(Exception x) {
+            System.err.println("FAILED: "+x);
+            throw new RuntimeException("Unexpected exception: "+x,x);
+        }
+    }
+
+
+    private static void checkQueue(
+            BlockingQueue<ContextObject<String,MBeanServerNotification>> q,
+                              String path, String type) {
+        try {
+          final ContextObject<String,MBeanServerNotification> ctxt =
+                    q.poll(500,TimeUnit.MILLISECONDS);
+          if (ctxt == null)
+            throw new RuntimeException("Timeout expired: expected notif from "+
+                    path +", type="+type);
+          if (!ctxt.name.equals(path))
+            throw new RuntimeException("expected notif from "+
+                    path +", got "+ctxt.name);
+          if (!ctxt.object.getType().equals(type))
+            throw new RuntimeException(ctxt.name+": expected type="+
+                    type +", got "+ctxt.object.getType());
+          if (!ctxt.object.getType().equals(type))
+            throw new RuntimeException(ctxt.name+": expected type="+
+                    type +", got "+ctxt.object.getType());
+          if (!ctxt.object.getMBeanName().equals(name("domain:type=MLet")))
+            throw new RuntimeException(ctxt.name+": expected MBean=domain:type=MLet"+
+                    ", got "+ctxt.object.getMBeanName());
+        } catch(InterruptedException x) {
+            throw new RuntimeException("unexpected interruption: "+x,x);
+        }
+    }
+
+    public static void main(String[] args) {
+        simpleTest(args);
+        simpleTest2();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NullDomainObjectNameTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ * @test NullDomainObjectNameTest.java
+ * @summary Test that null domains are correctly handled in namespaces.
+ * @author Daniel Fuchs
+ * @run clean NullDomainObjectNameTest Wombat WombatMBean
+ * @compile -XDignore.symbol.file=true  NullDomainObjectNameTest.java
+ * @run build NullDomainObjectNameTest Wombat WombatMBean
+ * @run main NullDomainObjectNameTest
+ */
+
+import com.sun.jmx.namespace.RoutingServerProxy;
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.logging.Logger;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXRemoteNamespace;
+import javax.management.namespace.JMXNamespace;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * Class NullDomainObjectNameTest
+ * @author Sun Microsystems, 2005 - All rights reserved.
+ */
+public class NullDomainObjectNameTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(NullDomainObjectNameTest.class.getName());
+
+    /** Creates a new instance of NullDomainObjectNameTest */
+    public NullDomainObjectNameTest() {
+    }
+
+    public static class MyWombat
+            extends Wombat {
+        public MyWombat() throws NotCompliantMBeanException {
+            super();
+        }
+        @Override
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+        throws Exception {
+
+            if (name == null)
+                name = new ObjectName(":type=Wombat");
+
+            return super.preRegister(server, name);
+        }
+
+    }
+
+    static String failure=null;
+
+    public static void testRegister() throws Exception {
+        final MBeanServer top = ManagementFactory.getPlatformMBeanServer();
+        final MBeanServer sub = MBeanServerFactory.createMBeanServer();
+        final JMXServiceURL url = new JMXServiceURL("rmi",null,0);
+        final JMXConnectorServer srv =
+                JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub);
+        srv.start();
+
+        try {
+
+            // Create a namespace rmi// that points to 'sub' and flows through
+            // a JMXRemoteNamespace connected to 'srv'
+            // The namespace rmi// will accept createMBean, but not registerMBean.
+            //
+            final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace.
+                    newJMXRemoteNamespace(srv.getAddress(),
+                    null);
+            top.registerMBean(rmiHandler,JMXNamespaces.getNamespaceObjectName("rmi"));
+            top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"),
+                    "connect", null, null);
+
+            // Create a namespace direct// that points to 'sub' and flows
+            // through a direct reference to 'sub'.
+            // The namespace direct// will accept createMBean, and registerMBean.
+            //
+            final JMXNamespace directHandler = new JMXNamespace(sub);
+            top.registerMBean(directHandler,
+                    JMXNamespaces.getNamespaceObjectName("direct"));
+
+            // Now cd to each of the created namespace.
+            //
+            MBeanServer cdrmi = JMXNamespaces.narrowToNamespace(top,"rmi");
+            MBeanServer cddirect = JMXNamespaces.narrowToNamespace(top,"direct");
+            boolean ok = false;
+
+            // Check that calling createMBean with a null domain works
+            // for namespace rmi//
+            //
+            try {
+                final ObjectInstance moi1 =
+                        cdrmi.createMBean(MyWombat.class.getName(),
+                        new ObjectName(":type=Wombat"));
+                System.out.println(moi1.getObjectName().toString()+
+                        ": created through rmi//");
+                assertEquals(moi1.getObjectName().getDomain(),
+                        cddirect.getDefaultDomain());
+                cddirect.unregisterMBean(moi1.getObjectName());
+            } catch (MBeanRegistrationException x) {
+                System.out.println("Received unexpected exception: " + x);
+                failed("Received unexpected exception: " + x);
+            }
+
+            // Check that calling refgisterMBean with a null domain works
+            // for namespace direct//
+            //
+            try {
+                final ObjectInstance moi2 =
+                        cddirect.registerMBean(new MyWombat(),
+                        new ObjectName(":type=Wombat"));
+                System.out.println(moi2.getObjectName().toString()+
+                        ": created through direct//");
+                assertEquals(moi2.getObjectName().getDomain(),
+                        cdrmi.getDefaultDomain());
+                cdrmi.unregisterMBean(moi2.getObjectName());
+            } catch (MBeanRegistrationException x) {
+                System.out.println("Received unexpected exception: " + x);
+                failed("Received unexpected exception: " + x);
+            }
+
+            // Now artificially pretend that 'sub' is contained in a faked//
+            // namespace.
+            //
+            RoutingServerProxy proxy =
+                    new RoutingServerProxy(sub, "", "faked", false);
+
+            // These should fail because the ObjectName doesn't start
+            // with "faked//"
+            try {
+                final ObjectInstance moi3 =
+                    proxy.registerMBean(new MyWombat(),
+                    new ObjectName(":type=Wombat"));
+                System.out.println(moi3.getObjectName().toString()+
+                    ": created through faked//");
+                failed("expected MBeanRegistrationException");
+            } catch (MBeanRegistrationException x) {
+                System.out.println("Received expected exception: " + x);
+                if (!(x.getCause() instanceof IllegalArgumentException)) {
+                    System.err.println("Bad wrapped exception: "+ x.getCause());
+                    failed("expected IllegalArgumentException");
+                }
+            }
+
+            // null should work with "faked//"
+            final ObjectInstance moi3 =
+                    proxy.registerMBean(new MyWombat(),null);
+            assertEquals(moi3.getObjectName().getDomain(),
+                         "faked//"+sub.getDefaultDomain());
+
+            System.out.println(moi3.getObjectName().toString() +
+                    ": created through faked//");
+
+            // Now check that null is correctly handled (accepted or rejected)
+            // in queries for each of the above configs.
+            //
+            ObjectName wombat = moi3.getObjectName().withDomain(
+                    moi3.getObjectName().getDomain().substring("faked//".length()));
+            ObjectInstance moi = new ObjectInstance(wombat,moi3.getClassName());
+
+            System.out.println("Checking queryNames(" +
+                    "new ObjectName(\":*\"),null) with rmi//");
+            assertEquals(cdrmi.queryNames(
+                    new ObjectName(":*"),null).contains(wombat),true);
+            System.out.println("Checking queryNames(" +
+                    "new ObjectName(\":*\"),null) with direct//");
+            assertEquals(cddirect.queryNames(
+                    new ObjectName(":*"),null).contains(wombat),true);
+            System.out.println("Checking queryMBeans(" +
+                    "new ObjectName(\":*\"),null) with rmi//");
+            assertEquals(cdrmi.queryMBeans(
+                    new ObjectName(":*"),null).contains(moi),true);
+            System.out.println("Checking queryMBeans(" +
+                    "new ObjectName(\":*\"),null) with direct//");
+            assertEquals(cddirect.queryMBeans(
+                    new ObjectName(":*"),null).contains(moi),true);
+
+            // These should fail because the ObjectName doesn't start
+            // with "faked//"
+            try {
+                System.out.println("Checking queryNames(" +
+                    "new ObjectName(\":*\"),null) with faked//");
+                assertEquals(proxy.queryNames(
+                        new ObjectName(":*"),null).
+                        contains(moi3.getObjectName()),true);
+                failed("queryNames(null,null) should have failed for faked//");
+            } catch (IllegalArgumentException x) {
+                System.out.println("Received expected exception for faked//: "+x);
+            }
+            // These should fail because the ObjectName doesn't start
+            // with "faked//"
+            try {
+                System.out.println("Checking queryMBeans(" +
+                    "new ObjectName(\":*\"),null) with faked//");
+                assertEquals(proxy.queryMBeans(
+                        new ObjectName(":*"),null).contains(moi3),true);
+                failed("queryMBeans(null,null) should have failed for faked//");
+            } catch (IllegalArgumentException x) {
+                System.out.println("Received expected exception for faked//: "+x);
+            }
+
+            System.out.println("Checking queryNames(faked//*:*,null)");
+            assertEquals(proxy.queryNames(new ObjectName("faked//*:*"),null).
+                    contains(moi3.getObjectName()),true);
+
+            System.out.println("Checking queryMBeans(faked//*:*,null)");
+            assertEquals(proxy.queryMBeans(new ObjectName("faked//*:*"),null).
+                    contains(moi3),true);
+
+            proxy.unregisterMBean(moi3.getObjectName());
+
+            // ADD NEW TESTS HERE ^^^
+
+        } finally {
+            srv.stop();
+        }
+
+        if (failure != null)
+            throw new Exception(failure);
+
+
+    }
+    private static void assertEquals(Object x, Object y) {
+        if (!equal(x, y))
+            failed("expected " + string(x) + "; got " + string(y));
+    }
+
+    private static boolean equal(Object x, Object y) {
+        if (x == y)
+            return true;
+        if (x == null || y == null)
+            return false;
+        if (x.getClass().isArray())
+            return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
+        return x.equals(y);
+    }
+
+    private static String string(Object x) {
+        String s = Arrays.deepToString(new Object[] {x});
+        return s.substring(1, s.length() - 1);
+    }
+
+
+    private static void failed(String why) {
+        failure = why;
+        new Throwable("FAILED: " + why).printStackTrace(System.out);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testRegister();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/NullObjectNameTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ * @test NullObjectNameTest.java
+ * @summary Test that null ObjectName are correctly handled in namespaces.
+ * @author Daniel Fuchs
+ * @run clean NullObjectNameTest Wombat WombatMBean
+ * @compile -XDignore.symbol.file=true  NullObjectNameTest.java
+ * @run build NullObjectNameTest Wombat WombatMBean
+ * @run main NullObjectNameTest
+ */
+
+import com.sun.jmx.namespace.RoutingServerProxy;
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.logging.Logger;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.JMXRemoteNamespace;
+import javax.management.namespace.JMXNamespace;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * Class NullObjectNameTest
+ * @author Sun Microsystems, 2005 - All rights reserved.
+ */
+public class NullObjectNameTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(NullObjectNameTest.class.getName());
+
+    /** Creates a new instance of NullObjectNameTest */
+    public NullObjectNameTest() {
+    }
+
+    public static class MyWombat
+            extends Wombat {
+        public MyWombat() throws NotCompliantMBeanException {
+            super();
+        }
+        @Override
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+        throws Exception {
+
+            if (name == null)
+                name = new ObjectName(":type=Wombat");
+
+            return super.preRegister(server, name);
+        }
+
+    }
+
+    static String failure=null;
+
+    public static void testRegister() throws Exception {
+        final MBeanServer top = ManagementFactory.getPlatformMBeanServer();
+        final MBeanServer sub = MBeanServerFactory.createMBeanServer();
+        final JMXServiceURL url = new JMXServiceURL("rmi",null,0);
+        final JMXConnectorServer srv =
+                JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub);
+        srv.start();
+
+        try {
+
+            // Create a namespace rmi// that points to 'sub' and flows through
+            // a JMXRemoteNamespace connected to 'srv'
+            // The namespace rmi// will accept createMBean, but not registerMBean.
+            //
+            final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace.
+                    newJMXRemoteNamespace(srv.getAddress(),null);
+            top.registerMBean(rmiHandler,JMXNamespaces.getNamespaceObjectName("rmi"));
+            top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"),
+                    "connect", null, null);
+
+            // Create a namespace direct// that points to 'sub' and flows
+            // through a direct reference to 'sub'.
+            // The namespace direct// will accept createMBean, and registerMBean.
+            //
+            final JMXNamespace directHandler = new JMXNamespace(sub);
+            top.registerMBean(directHandler,
+                    JMXNamespaces.getNamespaceObjectName("direct"));
+
+            // Now cd to each of the created namespace.
+            //
+            MBeanServer cdrmi = JMXNamespaces.narrowToNamespace(top,"rmi");
+            MBeanServer cddirect = JMXNamespaces.narrowToNamespace(top,"direct");
+            boolean ok = false;
+
+            // Check that calling createMBean with a null ObjectName fails
+            // gracefully for namespace rmi// (we can't add rmi// to a null
+            // ObjectName.
+            //
+            // TODO: do this test for all createMBean flavors!
+            try {
+                final ObjectInstance moi1 =
+                        cdrmi.createMBean(MyWombat.class.getName(),null);
+                System.out.println(moi1.getObjectName().toString()+
+                        ": created through rmi//");
+                cddirect.unregisterMBean(moi1.getObjectName());
+                failed("expected MBeanRegistrationException");
+            } catch (MBeanRegistrationException x) {
+                System.out.println("Received expected exception: " + x);
+                if (!(x.getCause() instanceof IllegalArgumentException)) {
+                    System.err.println("Bad wrapped exception: "+ x.getCause());
+                    failed("expected IllegalArgumentException");
+                }
+            }
+
+            // Check that calling refgisterMBean with a null ObjectName fails
+            // gracefully for namespace direct// (we can't add direct// to a null
+            // ObjectName.
+            //
+            try {
+                final ObjectInstance moi2 =
+                        cddirect.registerMBean(new MyWombat(), (ObjectName)null);
+                System.out.println(moi2.getObjectName().toString()+
+                        ": created through direct//");
+                cdrmi.unregisterMBean(moi2.getObjectName());
+                failed("expected MBeanRegistrationException");
+            } catch (MBeanRegistrationException x) {
+                System.out.println("Received expected exception: " + x);
+                if (!(x.getCause() instanceof IllegalArgumentException)) {
+                    System.err.println("Bad wrapped exception: "+ x.getCause());
+                    failed("expected IllegalArgumentException");
+                }
+            }
+
+            // Now artificially pretend that 'sub' is contained in a faked//
+            // namespace.
+            // We should be able to use 'null' in registerMBean/createMBean in
+            // this case.
+            //
+            RoutingServerProxy proxy =
+                    new RoutingServerProxy(sub,"","faked",false);
+            final ObjectInstance moi3 =
+                    proxy.registerMBean(new MyWombat(),null);
+            System.out.println(moi3.getObjectName().toString()+
+                    ": created through faked//");
+
+            // Now check that null is correctly handled (accepted or rejected)
+            // in queries for each of the above configs.
+            //
+            ObjectName wombat = moi3.getObjectName().withDomain(
+                    moi3.getObjectName().getDomain().substring("faked//".length()));
+            ObjectInstance moi = new ObjectInstance(wombat,moi3.getClassName());
+
+            System.out.println("Checking queryNames(null,null) with rmi//");
+            assertEquals(cdrmi.queryNames(null,null).contains(wombat),true);
+            System.out.println("Checking queryNames(null,null) with direct//");
+            assertEquals(cddirect.queryNames(null,null).contains(wombat),true);
+            System.out.println("Checking queryMBeans(null,null) with rmi//");
+            assertEquals(cdrmi.queryMBeans(null,null).contains(moi),true);
+            System.out.println("Checking queryMBeans(null,null) with direct//");
+            assertEquals(cddirect.queryMBeans(null,null).contains(moi),true);
+
+            try {
+                System.out.println("Checking queryNames(null,null) with faked//");
+                assertEquals(proxy.queryNames(null,null).
+                        contains(moi3.getObjectName()),true);
+                failed("queryNames(null,null) should have failed for faked//");
+            } catch (IllegalArgumentException x) {
+                System.out.println("Received expected exception for faked//: "+x);
+            }
+            try {
+                System.out.println("Checking queryMBeans(null,null) with faked//");
+                assertEquals(proxy.queryMBeans(null,null).contains(moi3),true);
+                failed("queryMBeans(null,null) should have failed for faked//");
+            } catch (IllegalArgumentException x) {
+                System.out.println("Received expected exception for faked//: "+x);
+            }
+            System.out.println("Checking queryNames(faked//*:*,null)");
+            assertEquals(proxy.queryNames(new ObjectName("faked//*:*"),null).
+                    contains(moi3.getObjectName()),true);
+
+            System.out.println("Checking queryMBeans(faked//*:*,null)");
+            assertEquals(proxy.queryMBeans(new ObjectName("faked//*:*"),null).
+                    contains(moi3),true);
+
+            proxy.unregisterMBean(moi3.getObjectName());
+
+            // ADD NEW TESTS HERE ^^^
+
+        } finally {
+            srv.stop();
+        }
+
+        if (failure != null)
+            throw new Exception(failure);
+
+
+    }
+    private static void assertEquals(Object x, Object y) {
+        if (!equal(x, y))
+            failed("expected " + string(x) + "; got " + string(y));
+    }
+
+    private static boolean equal(Object x, Object y) {
+        if (x == y)
+            return true;
+        if (x == null || y == null)
+            return false;
+        if (x.getClass().isArray())
+            return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
+        return x.equals(y);
+    }
+
+    private static String string(Object x) {
+        String s = Arrays.deepToString(new Object[] {x});
+        return s.substring(1, s.length() - 1);
+    }
+
+
+    private static void failed(String why) {
+        failure = why;
+        new Throwable("FAILED: " + why).printStackTrace(System.out);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testRegister();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/QueryNamesTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test QueryNamesTest.java 1.4
+ * @summary Test how queryNames works with Namespaces.
+ * @author Daniel Fuchs
+ * @run clean QueryNamesTest Wombat WombatMBean
+ * @run build QueryNamesTest Wombat WombatMBean
+ * @run main QueryNamesTest
+ */
+
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+
+/**
+ * Class QueryNamesTest
+ * @author Sun Microsystems, 2005 - All rights reserved.
+ */
+public class QueryNamesTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(QueryNamesTest.class.getName());
+
+    public static class LocalNamespace
+            extends JMXNamespace {
+
+        private static MBeanServer check(MBeanServer server) {
+            if (server == null)
+                throw new IllegalArgumentException("MBeanServer can't be null");
+            return server;
+        }
+
+        public LocalNamespace() {
+            this(MBeanServerFactory.createMBeanServer());
+        }
+
+        public LocalNamespace(MBeanServer server) {
+            super(check(server));
+        }
+
+
+        public static String add(MBeanServerConnection server,
+                String nspath)
+                throws IOException, JMException {
+            server.createMBean(LocalNamespace.class.getName(),
+                    JMXNamespaces.getNamespaceObjectName(nspath));
+            return nspath;
+        }
+    }
+
+    /** Creates a new instance of QueryNamesTest */
+    public QueryNamesTest() {
+    }
+
+    private static String[] namespaces = {
+        "greg", "greg//chichille", "greg//chichille//petard",
+        "greg//alambic", "greg//alambic//canette",
+        "greg//chichille/virgule", "greg//chichille/funeste",
+        "greg//chichille/virgule//bidouble",
+        "greg//chichille/virgule//bi/double",
+        "fran", "fran//gast", "fran//gast//gaf",
+        "fran//longtar", "fran//longtar//parcmetre"
+    };
+
+    private static void createNamespaces(MBeanServer server) throws Exception {
+        final LinkedList<String> all = new LinkedList<String>();
+        try {
+            for (String ns : namespaces)
+                all.addFirst(LocalNamespace.add(server,ns));
+        } catch (Exception e) {
+            removeNamespaces(server,all.toArray(new String[all.size()]));
+            throw e;
+        }
+    }
+
+    // Dummy test that checks that all JMXNamespaces are registered,
+    // but are not returned by queryNames("*:*");
+    //
+    private static void checkRegistration(MBeanServer server)
+        throws Exception {
+        final Set<ObjectName> handlerNames = new HashSet<ObjectName>(namespaces.length);
+        for (String ns : namespaces)
+            handlerNames.add(JMXNamespaces.getNamespaceObjectName(ns));
+        for (ObjectName nh : handlerNames) // check handler registration
+            if (!server.isRegistered(nh))
+                throw new InstanceNotFoundException("handler "+nh+
+                        " is not registered");
+
+        // global: queryNames("*:*") from top level
+        final Set<ObjectName> all1 = server.queryNames(null,null);
+        final Set<ObjectName> all2 = server.queryNames(ObjectName.WILDCARD,null);
+        if (!all1.equals(all2))
+            throw new Exception("queryNames(*:*) != queryNames(null)");
+        final Set<ObjectName> common = new HashSet<ObjectName>(all1);
+        common.retainAll(handlerNames);
+
+        final Set<ObjectName> ref = new HashSet<ObjectName>();
+        for (String ns : namespaces) {
+            if (!ns.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
+                ref.add(JMXNamespaces.getNamespaceObjectName(ns));
+        }
+        if (!common.equals(ref)) {
+            throw new Exception("some handler names were not returned by " +
+                    "wildcard query - only returned: "+common+
+                    ", expected: "+ref);
+        }
+
+        // for each namespace: queryNames("<namespace>//*:*");
+        for (String ns : namespaces) {
+            final ObjectName pattern = new ObjectName(ns+
+                    JMXNamespaces.NAMESPACE_SEPARATOR+"*:*");
+            final Set<ObjectName> all4 =
+                    server.queryNames(pattern,null);
+            final Set<ObjectName> common4 = new HashSet<ObjectName>(all4);
+            common4.retainAll(handlerNames);
+
+            final Set<ObjectName> ref4 = new HashSet<ObjectName>();
+            for (String ns2 : namespaces) {
+                if (! ns2.startsWith(ns+JMXNamespaces.NAMESPACE_SEPARATOR))
+                    continue;
+                if (!ns2.substring(ns.length()+
+                        JMXNamespaces.NAMESPACE_SEPARATOR.length()).
+                        contains(JMXNamespaces.NAMESPACE_SEPARATOR))
+                    ref4.add(JMXNamespaces.getNamespaceObjectName(ns2));
+            }
+            if (!common4.equals(ref4)) {
+                throw new Exception("some handler names were not returned by " +
+                    "wildcard query on "+pattern+" - only returned: "+common4+
+                    ", expected: "+ref4);
+            }
+        }
+    }
+
+    // Make a Map<parent, direct children>
+    private static Map<String,Set<String>> makeNsTree(String[] nslist) {
+        final Map<String,Set<String>> nsTree =
+                new LinkedHashMap<String,Set<String>>(nslist.length);
+        for (String ns : nslist) {
+            if (nsTree.get(ns) == null)
+                nsTree.put(ns,new LinkedHashSet<String>());
+            final String[] elts = ns.split(JMXNamespaces.NAMESPACE_SEPARATOR);
+            int last = ns.lastIndexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
+            if (last<0) continue;
+            while (last > 0 && ns.charAt(last-1) == '/') last--;
+            final String parent = ns.substring(0,last);
+            if (nsTree.get(parent) == null)
+                nsTree.put(parent,new LinkedHashSet<String>());
+            nsTree.get(parent).add(ns);
+        }
+        return nsTree;
+    }
+
+    private static class Rigolo {
+        final static String[] ones = { "a", "e", "i", "o", "u", "y", "ai", "oo",
+        "ae", "ey", "ay", "oy", "au", "ou", "eu", "oi", "ei", "ea"};
+        final static String[] twos = { "b", "bz", "c", "cz", "ch",
+        "ct", "ck", "cs", "d", "ds", "f",  "g", "gh", "h", "j", "k", "l", "m",
+        "n", "p", "ps", "q", "r", "s", "sh", "t", "v", "w", "x",
+        "z"};
+        final static String[] threes = {"rr","tt","pp","ss","dd","ff","ll", "mm", "nn",
+        "zz", "cc", "bb"};
+        final static String[] fours = {"x", "s", "ght", "cks", "rt", "rts", "ghts", "bs",
+          "ts", "gg" };
+        final static String[] fives = { "br", "bl", "cr", "cn", "cth", "dr",
+        "fr", "fl", "cl", "chr",  "gr", "gl", "kr", "kh", "pr", "pl", "ph",
+        "rh", "sr", "tr", "vr"};
+
+        private Random rg = new Random();
+
+        private String next(String[] table) {
+            return table[rg.nextInt(table.length)];
+        }
+
+        public String nextName(int max) {
+            final Random rg = new Random();
+            final int nl = 3 + rg.nextInt(max);
+            boolean begin = rg.nextBoolean();
+            StringBuilder sb = new StringBuilder();
+            for (int j = 0; j < nl ; j++) {
+                if (begin) {
+                    sb.append(next(ones));
+                } else if (j > 0 && j < nl-1 && rg.nextInt(4)==0) {
+                    sb.append(next(threes));
+                } else if (j < nl-1 && rg.nextInt(3)==0) {
+                    sb.append(next(fives));
+                } else {
+                    sb.append(next(twos));
+                }
+                begin = !begin;
+            }
+            if (!begin && rg.nextInt(2)==0)
+                sb.append(next(fours));
+            return sb.toString();
+        }
+
+        private ObjectName getWombatName(String ns, String domain, String name)
+            throws MalformedObjectNameException {
+            String d = domain;
+            if (ns != null && !ns.equals(""))
+                d = ns + JMXNamespaces.NAMESPACE_SEPARATOR + domain;
+            return new ObjectName(d+":type=Wombat,name="+name);
+        }
+
+        public Set<ObjectName> nextWombats(String ns)
+            throws MalformedObjectNameException  {
+            final int dcount = 1 + rg.nextInt(5);
+            final Set<ObjectName> wombats = new HashSet<ObjectName>();
+            for (int i = 0; i < dcount ; i++) {
+                final String d = nextName(7);
+                final int ncount = 5 + rg.nextInt(20);
+                for (int j = 0 ; j<ncount; j++) {
+                    final String n = nextName(5);
+                    wombats.add(getWombatName(ns,d,n));
+                }
+            }
+            return wombats;
+        }
+    }
+
+    public static void checkNsQuery(MBeanServer server)
+        throws Exception {
+        final Map<String,Set<String>> nsTree = makeNsTree(namespaces);
+        final Random rg = new Random();
+        final Rigolo rigolo = new Rigolo();
+        for (String ns : namespaces) {
+            final ObjectName name = JMXNamespaces.getNamespaceObjectName(ns);
+            final String[] doms =
+                    (String[])server.getAttribute(name,"Domains");
+            final Set<String> subs = new HashSet<String>();
+            for (String d : doms) {
+                if (d.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
+                    subs.add(ns+JMXNamespaces.NAMESPACE_SEPARATOR+d.substring(0,
+                            d.length()-JMXNamespaces.NAMESPACE_SEPARATOR.length()));
+                }
+            }
+
+            final Set<String> expectNs = new HashSet<String>(nsTree.get(ns));
+
+            if (! subs.containsAll(expectNs))
+                throw new Exception("getDomains didn't return all namespaces: "+
+                        "returned="+subs+", expected="+expectNs);
+            if (! expectNs.containsAll(subs))
+                throw new Exception("getDomains returned additional namespaces: "+
+                        "returned="+subs+", expected="+expectNs);
+
+            final Set<ObjectName> nsNames = server.queryNames(
+                    new ObjectName(ns+
+                    JMXNamespaces.NAMESPACE_SEPARATOR+"*"+
+                    JMXNamespaces.NAMESPACE_SEPARATOR+":*"),null);
+
+            final Set<ObjectName> expect =
+                    new HashSet<ObjectName>(expectNs.size());
+            for (String sub : expectNs) {
+                expect.add(JMXNamespaces.getNamespaceObjectName(sub));
+            }
+
+            if (! nsNames.containsAll(expect))
+                throw new Exception("queryNames didn't return all namespaces: "+
+                        "returned="+nsNames+", expected="+expect);
+            if (! expect.containsAll(nsNames))
+                throw new Exception("getDomains returned additional namespaces: "+
+                        "returned="+nsNames+", expected="+expect);
+
+        }
+    }
+
+    private static void addWombats(MBeanServer server, Set<ObjectName> names)
+        throws Exception {
+        for (ObjectName on : names) {
+            if (! server.isRegistered(on)) {
+                server.createMBean(Wombat.class.getName(),on);
+                System.out.println("A new wombat is born: "+on);
+            }
+        }
+    }
+
+    private static void addWombats(MBeanServer server,
+             Map<String,Set<ObjectName>> wombats)
+        throws Exception {
+        for (String ns : wombats.keySet()) {
+            addWombats(server,wombats.get(ns));
+        }
+    }
+
+    private static Map<String,Set<ObjectName>> nameWombats()
+        throws Exception {
+        final Rigolo rigolo = new Rigolo();
+        final Map<String,Set<ObjectName>> wombats =
+                new HashMap<String,Set<ObjectName>>(namespaces.length);
+
+        for (String ns : namespaces) {
+            wombats.put(ns,rigolo.nextWombats(ns));
+        }
+        wombats.put("",rigolo.nextWombats(""));
+        return wombats;
+    }
+
+    private static boolean removeWombats(MBeanServer server,
+            Map<String,Set<ObjectName>> wombats) {
+        boolean res = true;
+        for (String ns : wombats.keySet()) {
+            res = res && removeWombats(server,wombats.get(ns));
+        }
+        return res;
+    }
+
+    private static boolean removeWombats(MBeanServer server,
+            Set<ObjectName> wombats) {
+        boolean res = true;
+        for (ObjectName on : wombats) {
+            try {
+                if (server.isRegistered(on))
+                    server.unregisterMBean(on);
+            } catch (Exception x) {
+                res = false;
+                System.out.println("Failed to remove "+on+": "+x);
+            }
+        }
+        return res;
+    }
+
+    public static void main(String[] args)
+        throws Exception {
+        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+        Map<String,Set<ObjectName>>  wombats = nameWombats();
+        createNamespaces(server);
+        try {
+            addWombats(server,wombats);
+            System.out.println("MBeans: " +server.getMBeanCount());
+            System.out.println("Visible: " +server.queryNames(null,null).size());
+            System.out.println("Domains: " +Arrays.asList(server.getDomains()));
+            checkRegistration(server);
+            checkNsQuery(server);
+        } finally {
+            boolean res = true;
+            res = res && removeWombats(server, wombats);
+            if (!res)
+                throw new RuntimeException("failed to cleanup some namespaces");
+        }
+
+    }
+
+    private static boolean removeNamespaces(MBeanServer server) {
+        final List<String> l = Arrays.asList(namespaces);
+        Collections.reverse(l);
+        return removeNamespaces(server, l.toArray(new String[namespaces.length]));
+    }
+
+    private static boolean removeNamespaces(MBeanServer server, String[] t) {
+        boolean success = true;
+        for (String ns : t) {
+            try {
+                server.unregisterMBean(JMXNamespaces.getNamespaceObjectName(ns));
+            } catch (Exception x) {
+                System.out.println("failed to remove namespace: "+ ns);
+                success = false;
+            }
+        }
+        return success;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/RemoveNotificationListenerTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test RemoveNotificationListenerTest.java 1.8
+ * @summary General RemoveNotificationListenerTest test.
+ * @author Daniel Fuchs
+ * @run clean RemoveNotificationListenerTest JMXRemoteTargetNamespace
+ * @compile -XDignore.symbol.file=true  JMXRemoteTargetNamespace.java
+ * @run build RemoveNotificationListenerTest JMXRemoteTargetNamespace
+ * @run main/othervm RemoveNotificationListenerTest
+ */
+
+import com.sun.jmx.remote.util.EventClientConnection;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+import javax.management.JMException;
+import javax.management.JMX;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerFactory;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.remote.JMXAuthenticator;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXConnectorServerMBean;
+import javax.management.remote.JMXPrincipal;
+import javax.management.remote.JMXServiceURL;
+import javax.security.auth.Subject;
+
+/**
+ * Class RemoveNotificationListenerTest
+ */
+public class RemoveNotificationListenerTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(RemoveNotificationListenerTest.class.getName());
+
+    /** Creates a new instance of RemoveNotificationListenerTest */
+    public RemoveNotificationListenerTest() {
+    }
+
+    public static class SubjectAuthenticator implements JMXAuthenticator {
+        final Set<Subject> authorized;
+        public SubjectAuthenticator(Subject[] authorized) {
+            this.authorized = new HashSet<Subject>(Arrays.asList(authorized));
+        }
+
+        public Subject authenticate(Object credentials) {
+            if (authorized.contains(credentials))
+                return (Subject)credentials;
+            else
+                throw new SecurityException("Subject not authorized: "+credentials);
+        }
+
+    }
+
+    public static interface LongtarMBean {
+        public void sendNotification(Object userData)
+            throws IOException, JMException;
+    }
+    public static class Longtar extends NotificationBroadcasterSupport
+            implements LongtarMBean {
+        public Longtar() {
+            super(new MBeanNotificationInfo[] {
+                new MBeanNotificationInfo(new String[] {"papillon"},
+                        "pv","M'enfin???")
+            });
+        }
+
+        public void sendNotification(Object userData)
+        throws IOException, JMException {
+            final Notification n =
+                    new Notification("papillon",this,nextseq(),"M'enfin???");
+            n.setUserData(userData);
+            System.out.println("Sending notification: "+userData);
+            sendNotification(n);
+        }
+
+        private static synchronized long nextseq() {return ++seqnb;}
+        private static volatile long seqnb=0;
+    }
+
+    private static final String NS = JMXNamespaces.NAMESPACE_SEPARATOR;
+    private static final String CS = "jmx.rmi:type=JMXConnectorServer";
+    private static final String BD = "longtar:type=Longtar";
+
+    private static void createNamespace(MBeanServerConnection server,
+            String namespace, Subject creator, boolean forwarding)
+            throws Exception {
+        final MBeanServer sub = MBeanServerFactory.createMBeanServer();
+        final JMXServiceURL url = new JMXServiceURL("rmi",null,0);
+        final Map<String,Object> smap = new HashMap<String,Object>();
+        smap.put(JMXConnectorServer.AUTHENTICATOR,
+                new SubjectAuthenticator(new Subject[] {creator}));
+        final JMXConnectorServer rmi =
+                JMXConnectorServerFactory.newJMXConnectorServer(url,smap,null);
+        final ObjectName name = new ObjectName(CS);
+        sub.registerMBean(rmi,name);
+        rmi.start();
+        final Map<String,Object> cmap = new HashMap<String,Object>();
+        cmap.put(JMXConnector.CREDENTIALS,creator);
+        final Map<String,Object> options = new HashMap<String,Object>(cmap);
+        options.put(JMXRemoteTargetNamespace.CREATE_EVENT_CLIENT,"true");
+        JMXRemoteTargetNamespace.createNamespace(server,
+                namespace,
+                rmi.getAddress(),
+                options
+                );
+        server.invoke(JMXNamespaces.getNamespaceObjectName(namespace),
+                "connect", null,null);
+    }
+    private static void closeNamespace(MBeanServerConnection server,
+            String namespace) {
+        try {
+            final ObjectName hname =
+                    JMXNamespaces.getNamespaceObjectName(namespace);
+            if (!server.isRegistered(hname))
+                return;
+            final ObjectName sname =
+                    new ObjectName(namespace+NS+CS);
+            if (!server.isRegistered(sname))
+                return;
+            final JMXConnectorServerMBean cs =
+                    JMX.newMBeanProxy(server,sname,
+                    JMXConnectorServerMBean.class,true);
+            try {
+                cs.stop();
+            } finally {
+                server.unregisterMBean(hname);
+            }
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    private static Subject newSubject(String[] principals) {
+        final Set<Principal> ps = new HashSet<Principal>();
+        for (String p:principals) ps.add(new JMXPrincipal(p));
+        return new Subject(true,ps,Collections.emptySet(),Collections.emptySet());
+    }
+
+
+    public static void testSubject() throws Exception {
+        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+        final String a = "a";
+        final String b = a + NS + "b";
+
+        final Subject s1 = newSubject(new String[] {"chichille"});
+        final Subject s2 = newSubject(new String[] {"alambic"});
+        final Subject s3 = newSubject(new String[] {"virgule"});
+        final Subject s4 = newSubject(new String[] {"funeste"});
+
+        final JMXServiceURL url = new JMXServiceURL("rmi",null,0);
+        final Map<String,Object> smap = new HashMap<String,Object>();
+        smap.put(JMXConnectorServer.AUTHENTICATOR,
+                new SubjectAuthenticator(new Subject[] {s1}));
+        final JMXConnectorServer rmi =
+                JMXConnectorServerFactory.newJMXConnectorServer(url,smap,null);
+        final ObjectName name = new ObjectName(CS);
+        server.registerMBean(rmi,name);
+        rmi.start();
+
+        try {
+
+            final Map<String,Object> map = new HashMap<String,Object>();
+            map.put(JMXConnector.CREDENTIALS,s1);
+            final JMXConnector c =
+                    JMXConnectorFactory.connect(rmi.getAddress(),map);
+            final MBeanServerConnection mbsorig = c.getMBeanServerConnection();
+
+            final MBeanServerConnection mbs =
+                    EventClientConnection.getEventConnectionFor(mbsorig,null);
+
+            createNamespace(mbs,a,s2,true);
+            createNamespace(mbs,b,s3,true);
+
+            final ObjectName longtar = new ObjectName(b+NS+BD);
+
+            mbs.createMBean(Longtar.class.getName(),longtar);
+            final LongtarMBean proxy =
+                    JMX.newMBeanProxy(mbs,longtar,LongtarMBean.class,true);
+
+
+            final BlockingQueue<Notification> bbq =
+                    new ArrayBlockingQueue<Notification>(10);
+            final NotificationListener listener1 = new NotificationListener() {
+                public void handleNotification(Notification notification,
+                        Object handback) {
+                    System.out.println(notification.getSequenceNumber()+": "+
+                            notification.getMessage());
+                    bbq.add(notification);
+                }
+            };
+            final NotificationListener listener2 = new NotificationListener() {
+                public void handleNotification(Notification notification,
+                        Object handback) {
+                    System.out.println(notification.getSequenceNumber()+": "+
+                            notification.getMessage());
+                    bbq.add(notification);
+                }
+            };
+
+            final NotificationEmitter ubpdalfdla = (NotificationEmitter)proxy;
+            try {
+
+                // Add 1 NL, send 1 notif (1)
+                ubpdalfdla.addNotificationListener(listener1,null,listener1);
+                proxy.sendNotification(new Integer(1));
+                // Thread.sleep(180000);
+
+                // We should have 1 notif with userdata = 1
+                final Notification n1 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n1.getUserData()).intValue() != 1)
+                    throw new Exception("Expected 1, got"+n1.getUserData());
+
+                // remove NL, send 1 notif (2) => we shouldn't receive it
+                ubpdalfdla.removeNotificationListener(listener1,null,listener1);
+                proxy.sendNotification(new Integer(2));
+
+                // add NL, send 1 notif (3)
+                ubpdalfdla.addNotificationListener(listener1,null,listener1);
+                proxy.sendNotification(new Integer(3));
+
+                // we should receive only 1 notif (3)
+                final Notification n3 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n3.getUserData()).intValue() != 3)
+                    throw new Exception("Expected 3, got"+n3.getUserData());
+
+                // remove NL, send 1 notif (4) => we shouldn't receive it.
+                ubpdalfdla.removeNotificationListener(listener1);
+                proxy.sendNotification(new Integer(4));
+
+                // add NL, send 1 notif (5).
+                ubpdalfdla.addNotificationListener(listener1,null,listener1);
+                proxy.sendNotification(new Integer(5));
+
+                // next notif in queue should be (5)
+                final Notification n5 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n5.getUserData()).intValue() != 5)
+                    throw new Exception("Expected 5, got"+n5.getUserData());
+
+                // add 2 NL, send 1 notif (6)
+                ubpdalfdla.addNotificationListener(listener2,null,listener2);
+                ubpdalfdla.addNotificationListener(listener2,null,null);
+                proxy.sendNotification(new Integer(6));
+
+                // We have 3 NL, we should receive (6) 3 times....
+                final Notification n61 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n61.getUserData()).intValue() != 6)
+                    throw new Exception("Expected 6 (#1), got"+n61.getUserData());
+                final Notification n62 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n62.getUserData()).intValue() != 6)
+                    throw new Exception("Expected 6 (#2), got"+n62.getUserData());
+                final Notification n63 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n63.getUserData()).intValue() != 6)
+                    throw new Exception("Expected 6 (#3), got"+n63.getUserData());
+
+                // Remove 1 NL, send 1 notif (7)
+                ubpdalfdla.removeNotificationListener(listener2,null,null);
+                proxy.sendNotification(new Integer(7));
+
+                // next notifs in queue should be (7), twice...
+                final Notification n71 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n71.getUserData()).intValue() != 7)
+                    throw new Exception("Expected 7 (#1), got"+n71.getUserData());
+                final Notification n72 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n72.getUserData()).intValue() != 7)
+                    throw new Exception("Expected 7 (#2), got"+n72.getUserData());
+
+                // Add 1 NL, send 1 notif (8)
+                ubpdalfdla.addNotificationListener(listener2,null,null);
+                proxy.sendNotification(new Integer(8));
+
+                // Next notifs in queue should be (8), 3 times.
+                final Notification n81 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n81.getUserData()).intValue() != 8)
+                    throw new Exception("Expected 8 (#1), got"+n81.getUserData());
+                final Notification n82 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n82.getUserData()).intValue() != 8)
+                    throw new Exception("Expected 8 (#2), got"+n82.getUserData());
+                final Notification n83 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n83.getUserData()).intValue() != 8)
+                    throw new Exception("Expected 8 (#3), got"+n83.getUserData());
+
+                // Remove 2 NL, send 1 notif (9)
+                ubpdalfdla.removeNotificationListener(listener2);
+                proxy.sendNotification(new Integer(9));
+
+                // Next notifs in queue should be (9), 1 time only.
+                final Notification n9 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n9.getUserData()).intValue() != 9)
+                    throw new Exception("Expected 9, got"+n9.getUserData());
+
+                // send 1 notif (10)
+                proxy.sendNotification(new Integer(10));
+
+                // Next notifs in queue should be (10), 1 time only.
+                final Notification n10 = bbq.poll(3,TimeUnit.SECONDS);
+                // may throw NPE => would indicate a bug.
+                if (((Integer)n10.getUserData()).intValue() != 10)
+                    throw new Exception("Expected 10, got"+n10.getUserData());
+
+                ubpdalfdla.removeNotificationListener(listener1);
+                mbs.unregisterMBean(longtar);
+
+            } finally {
+                c.close();
+            }
+        } finally {
+            closeNamespace(server,b);
+            closeNamespace(server,a);
+            rmi.stop();
+        }
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        testSubject();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/RoutingServerProxyTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,399 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test RoutingServerProxyTest.java 1.6
+ * @summary General RoutingServerProxyTest test.
+ * @author Daniel Fuchs
+ * @run clean RoutingServerProxyTest Wombat WombatMBean
+ * @compile -XDignore.symbol.file=true RoutingServerProxyTest.java
+ * @run build RoutingServerProxyTest Wombat WombatMBean
+ * @run main RoutingServerProxyTest
+ */
+
+import com.sun.jmx.namespace.RoutingServerProxy;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.JMX;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationEmitter;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.StandardEmitterMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.MBeanServerSupport;
+
+/**
+ * Class RoutingServerProxyTest
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class RoutingServerProxyTest {
+
+    /**
+     * A logger for this class.
+     **/
+    private static final Logger LOG =
+            Logger.getLogger(RoutingServerProxyTest.class.getName());
+
+    /**
+     * Creates a new instance of RoutingServerProxyTest
+     */
+    public RoutingServerProxyTest() {
+    }
+
+    public static class DynamicWombat extends StandardEmitterMBean {
+        DynamicWombat(Wombat w) throws NotCompliantMBeanException {
+            super(w,WombatMBean.class,w);
+        }
+
+        @Override
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+            throws Exception {
+            final ObjectName myname = ((Wombat)getImplementation()).
+                    preRegister(server,name);
+            return super.preRegister(server,myname);
+        }
+
+        @Override
+        public void postRegister(Boolean registrationDone) {
+            try {
+                ((Wombat)getImplementation()).
+                    postRegister(registrationDone);
+            } finally {
+                super.postRegister(registrationDone);
+            }
+        }
+
+        @Override
+        public void preDeregister() throws Exception {
+                ((Wombat)getImplementation()).
+                    preDeregister();
+                super.preDeregister();
+
+        }
+
+        @Override
+        public void postDeregister() {
+            try {
+                ((Wombat)getImplementation()).
+                    postDeregister();
+            } finally {
+                super.postDeregister();
+            }
+        }
+    }
+
+    public static class VirtualWombatHandler
+            extends JMXNamespace {
+
+        public static class VirtualWombatRepository
+                extends MBeanServerSupport {
+
+            final Map<ObjectName, DynamicMBean> bush;
+
+            VirtualWombatRepository(Map<ObjectName, DynamicMBean> bush) {
+                this.bush = bush;
+            }
+
+            @Override
+            protected Set<ObjectName> getNames() {
+                return bush.keySet();
+            }
+
+            @Override
+            public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                    throws InstanceNotFoundException {
+                final DynamicMBean mb = bush.get(name);
+                if (mb == null) {
+                    throw new InstanceNotFoundException(String.valueOf(name));
+                }
+                return mb;
+            }
+
+            @Override
+            public NotificationEmitter getNotificationEmitterFor(
+                    ObjectName name) throws InstanceNotFoundException {
+                DynamicMBean mbean = getDynamicMBeanFor(name);
+                if (mbean instanceof NotificationEmitter) {
+                    return (NotificationEmitter) mbean;
+                }
+                return null;
+            }
+        }
+        VirtualWombatRepository bush;
+
+        VirtualWombatHandler(Map<ObjectName, DynamicMBean> bush) {
+            this(new VirtualWombatRepository(Collections.synchronizedMap(bush)));
+        }
+
+        private VirtualWombatHandler(VirtualWombatRepository repository) {
+            super(repository);
+            bush = repository;
+        }
+
+        @Override
+        public ObjectName preRegister(MBeanServer server, ObjectName name)
+                throws Exception {
+            final ObjectName myname = super.preRegister(server, name);
+            return myname;
+        }
+
+        @Override
+        public void postRegister(Boolean registrationDone) {
+            if (!registrationDone.booleanValue()) {
+                return;
+            }
+            final MBeanServer me = JMXNamespaces.narrowToNamespace(getMBeanServer(),
+                    getObjectName().getDomain());
+            for (Map.Entry<ObjectName, DynamicMBean> e : bush.bush.entrySet()) {
+                final DynamicMBean obj = e.getValue();
+                try {
+                    if (obj instanceof MBeanRegistration) {
+                        ((MBeanRegistration) obj).preRegister(me, e.getKey());
+                    }
+                } catch (Exception x) {
+                    System.err.println("preRegister failed for " +
+                            e.getKey() + ": " + x);
+                    bush.bush.remove(e.getKey());
+                }
+            }
+            for (Map.Entry<ObjectName, DynamicMBean> e : bush.bush.entrySet()) {
+                final Object obj = e.getValue();
+                if (obj instanceof MBeanRegistration) {
+                    ((MBeanRegistration) obj).postRegister(registrationDone);
+                }
+            }
+        }
+
+        @Override
+        public void preDeregister() throws Exception {
+            for (Map.Entry<ObjectName, DynamicMBean> e : bush.bush.entrySet()) {
+                final Object obj = e.getValue();
+                if (obj instanceof MBeanRegistration) {
+                    ((MBeanRegistration) obj).preDeregister();
+                }
+            }
+        }
+
+        @Override
+        public void postDeregister() {
+            for (Map.Entry<ObjectName, DynamicMBean> e : bush.bush.entrySet()) {
+                final Object obj = e.getValue();
+                if (obj instanceof MBeanRegistration) {
+                    ((MBeanRegistration) obj).postDeregister();
+                }
+            }
+        }
+    }
+
+    public static ObjectName getWombatName(String name)
+        throws MalformedObjectNameException {
+        return ObjectName.getInstance("australian.bush:type=Wombat,name="+name);
+    }
+
+    public static ObjectName addDir(String dir, ObjectName name)
+        throws MalformedObjectNameException {
+        return name.withDomain(
+                dir+JMXNamespaces.NAMESPACE_SEPARATOR+ name.getDomain());
+    }
+
+    public static void simpleTest()
+        throws JMException, IOException {
+        final MBeanServer master = MBeanServerFactory.createMBeanServer();
+        final MBeanServer agent1 = MBeanServerFactory.createMBeanServer();
+        final Wombat w1 = new Wombat();
+        final Wombat w2 = new Wombat();
+        final Wombat w3 = new Wombat();
+        final Map<ObjectName,DynamicMBean> wombats =
+                new ConcurrentHashMap<ObjectName,DynamicMBean>();
+        wombats.put(getWombatName("LittleWombat"),
+                new DynamicWombat(w2));
+        wombats.put(getWombatName("BigWombat"),
+                new DynamicWombat(w3));
+        final Wombat w4 = new Wombat();
+        final Wombat w5 = new Wombat();
+
+        final JMXNamespace agent2 =
+                new VirtualWombatHandler(wombats);
+        agent1.registerMBean(w4,getWombatName("LittleWombat"));
+        master.registerMBean(w1,getWombatName("LittleWombat"));
+        master.registerMBean(new JMXNamespace(agent1),
+                JMXNamespaces.getNamespaceObjectName("south.east"));
+        master.registerMBean(agent2,
+                JMXNamespaces.getNamespaceObjectName("north"));
+        master.registerMBean(w5,addDir("south.east",
+                getWombatName("GrandWombat")));
+
+        MBeanServer se = null;
+
+        try {
+            se = JMXNamespaces.narrowToNamespace(master,"south.easht");
+        } catch (Exception x) {
+            System.out.println("Caught expected exception: "+x);
+        }
+        if (se != null)
+            throw new RuntimeException("Expected exception for "+
+                    "cd(south.easht)");
+        se = JMXNamespaces.narrowToNamespace(master,"south.east");
+
+        MBeanServer nth = JMXNamespaces.narrowToNamespace(master,"north");
+
+        final ObjectName ln = getWombatName("LittleWombat");
+        MBeanInfo mb1 = master.getMBeanInfo(ln);
+        MBeanInfo mb2 = se.getMBeanInfo(ln);
+        MBeanInfo mb3 = nth.getMBeanInfo(ln);
+
+        final WombatMBean grand = JMX.newMBeanProxy(se,
+                getWombatName("GrandWombat"),WombatMBean.class);
+        final WombatMBean big = JMX.newMBeanProxy(nth,
+                getWombatName("BigWombat"),WombatMBean.class);
+        grand.getCaption();
+        big.getCaption();
+        grand.setCaption("I am GrandWombat");
+        big.setCaption("I am BigWombat");
+
+        final WombatMBean grand2 =
+                JMX.newMBeanProxy(master,addDir("south.east",
+                getWombatName("GrandWombat")),WombatMBean.class);
+        final WombatMBean big2 =
+                JMX.newMBeanProxy(master,addDir("north",
+                getWombatName("BigWombat")),WombatMBean.class);
+        if (!"I am GrandWombat".equals(grand2.getCaption()))
+            throw new RuntimeException("bad caption for GrandWombat"+
+                    grand2.getCaption());
+        if (!"I am BigWombat".equals(big2.getCaption()))
+            throw new RuntimeException("bad caption for BigWombat"+
+                    big2.getCaption());
+
+
+        final Set<ObjectInstance> northWombats =
+                nth.queryMBeans(ObjectName.WILDCARD,null);
+        final Set<ObjectInstance> seWombats =
+                se.queryMBeans(ObjectName.WILDCARD,null);
+        if (!northWombats.equals(
+                agent2.getSourceServer().queryMBeans(ObjectName.WILDCARD,null))) {
+            throw new RuntimeException("Bad Wombat census in northern territory: got "
+                    +northWombats+", expected "+
+                    agent2.getSourceServer().
+                    queryMBeans(ObjectName.WILDCARD,null));
+        }
+        if (!seWombats.equals(
+                agent1.queryMBeans(ObjectName.WILDCARD,null))) {
+            throw new RuntimeException("Bad Wombat census in south east: got "
+                    +seWombats+", expected "+
+                    agent1.
+                    queryMBeans(ObjectName.WILDCARD,null));
+        }
+
+        final MBeanServer supermaster = MBeanServerFactory.createMBeanServer();
+        supermaster.registerMBean(new JMXNamespace(master),
+            JMXNamespaces.getNamespaceObjectName("australia"));
+        final MBeanServer proxymaster =
+                JMXNamespaces.narrowToNamespace(supermaster,"australia");
+        final MBeanServer sem =
+                JMXNamespaces.narrowToNamespace(proxymaster,"south.east");
+        final MBeanServer nthm =
+                JMXNamespaces.narrowToNamespace(proxymaster,"north");
+        final Set<ObjectInstance> northWombats2 =
+                nthm.queryMBeans(ObjectName.WILDCARD,null);
+        final Set<ObjectInstance> seWombats2 =
+                sem.queryMBeans(ObjectName.WILDCARD,null);
+        if (!northWombats2.equals(
+                agent2.getSourceServer().queryMBeans(ObjectName.WILDCARD,null))) {
+            throw new RuntimeException("Bad Wombat census in " +
+                    "Australia // North");
+        }
+        if (!seWombats2.equals(
+                agent1.queryMBeans(ObjectName.WILDCARD,null))) {
+            throw new RuntimeException("Bad Wombat census in " +
+                    "Australia // South East");
+        }
+        final WombatMBean grand3 =
+                JMX.newMBeanProxy(supermaster,
+                addDir("australia//south.east",
+                getWombatName("GrandWombat")),WombatMBean.class);
+        final WombatMBean big3 =
+                JMX.newMBeanProxy(supermaster,addDir("australia//north",
+                getWombatName("BigWombat")),WombatMBean.class);
+        if (!"I am GrandWombat".equals(grand3.getCaption()))
+            throw new RuntimeException("bad caption for " +
+                    "australia//south.east//GrandWombat"+
+                    grand3.getCaption());
+        if (!"I am BigWombat".equals(big3.getCaption()))
+            throw new RuntimeException("bad caption for " +
+                    "australia//north//BigWombat"+
+                    big3.getCaption());
+        final WombatMBean grand4 =
+                JMX.newMBeanProxy(sem,
+                getWombatName("GrandWombat"),WombatMBean.class);
+        final WombatMBean big4 =
+                JMX.newMBeanProxy(nthm,
+                getWombatName("BigWombat"),WombatMBean.class);
+        if (!"I am GrandWombat".equals(grand4.getCaption()))
+            throw new RuntimeException("bad caption for " +
+                    "[australia//south.east//] GrandWombat"+
+                    grand4.getCaption());
+        if (!"I am BigWombat".equals(big4.getCaption()))
+            throw new RuntimeException("bad caption for " +
+                    "[australia//north//] BigWombat"+
+                    big4.getCaption());
+
+        if (!(nthm instanceof RoutingServerProxy))
+            throw new AssertionError("expected RoutingServerProxy for nthm");
+        if (!(sem instanceof RoutingServerProxy))
+            throw new AssertionError("expected RoutingServerProxy for sem");
+
+        if (!"australia//north".equals((
+                (RoutingServerProxy)nthm).getSourceNamespace()))
+            throw new RuntimeException("north territory should be in australia");
+        if (!"australia//south.east".equals((
+                (RoutingServerProxy)sem).getSourceNamespace()))
+            throw new RuntimeException("south east territory should be in australia");
+
+    }
+
+    public static  void main(String[] args) {
+        try {
+            simpleTest();
+        } catch (Exception x) {
+            System.err.println("SimpleTest failed: "+x);
+            throw new RuntimeException(x);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/SerialParamProcessorTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,571 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test SerialParamProcessorTest.java 1.8
+ * @summary General SerialParamProcessorTest test.
+ * @author Daniel Fuchs
+ * @run clean SerialParamProcessorTest Wombat WombatMBean
+ * @compile -XDignore.symbol.file=true  SerialParamProcessorTest.java
+ * @run build SerialParamProcessorTest Wombat WombatMBean
+ * @run main SerialParamProcessorTest
+ */
+
+import com.sun.jmx.namespace.serial.RewritingProcessor;
+import java.beans.ConstructorProperties;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import javax.management.AttributeChangeNotification;
+import javax.management.AttributeList;
+import javax.management.JMException;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+/**
+ * Class SerialParamProcessorTest
+ *
+ * @author Sun Microsystems, Inc.
+ */
+public class SerialParamProcessorTest {
+
+    /**
+     * Creates a new instance of SerialParamProcessorTest
+     */
+    public SerialParamProcessorTest() {
+    }
+
+    public static class MyCompositeData implements Serializable {
+        private static final long serialVersionUID = 3186492415099133506L;
+        public MyCompositeData(ObjectName foobar,ObjectName absolute,
+                long count, String name) {
+            this(foobar,absolute,count,name,new ObjectName[]{foobar,absolute});
+        }
+        @ConstructorProperties(value={"fooBar","absolute","count","name",
+                                        "allNames"})
+        public MyCompositeData(ObjectName foobar,ObjectName absolute,
+                long count, String name, ObjectName[] allnames) {
+            this.foobar = foobar;
+            this.absolute = absolute;
+            this.count = count;
+            this.name = name;
+            this.allnames = allnames;
+        }
+        ObjectName foobar,absolute,allnames[];
+        long count;
+        String name;
+        public ObjectName getFooBar() {
+            return foobar;
+        }
+        public ObjectName getAbsolute() {
+            return absolute;
+        }
+        public ObjectName[] getAllNames() {
+            return allnames;
+        }
+        public long getCount() {
+            return count;
+        }
+        public String getName() {
+            return name;
+        }
+        private Object[] toArray() {
+            final Object[] props = {
+                getName(),getFooBar(),getAbsolute(),getAllNames(),getCount()
+            };
+            return props;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof MyCompositeData)
+                return Arrays.deepEquals(toArray(),
+                        ((MyCompositeData)o).toArray());
+            return false;
+        }
+        @Override
+        public int hashCode() {
+            return Arrays.deepHashCode(toArray());
+        }
+    }
+
+    public static interface MyMXBean {
+        public Map<String,MyCompositeData> getAll();
+        public MyCompositeData lookup(String name);
+        public void put(String name, MyCompositeData data);
+        public MyCompositeData remove(String name);
+    }
+
+    public static class My implements MyMXBean {
+        Map<String,MyCompositeData> datas =
+                new HashMap<String,MyCompositeData>();
+        public Map<String,MyCompositeData> getAll() {
+            return datas;
+        }
+        public MyCompositeData lookup(String name) {
+            return datas.get(name);
+        }
+        public void put(String name, MyCompositeData data) {
+            datas.put(name,data);
+        }
+        public MyCompositeData remove(String name) {
+            return datas.remove(name);
+        }
+    }
+
+    public static class BandicootClass implements Serializable {
+        private static final long serialVersionUID = -5494055748633966355L;
+        public final Object gloups;
+        public BandicootClass(Object gloups) {
+            this.gloups = gloups;
+        }
+        private Object[] toArray() {
+            final Object[] one = {gloups};
+            return one;
+        }
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof BandicootClass)) return false;
+            final Object[] one = {gloups};
+            return Arrays.deepEquals(toArray(),((BandicootClass)obj).toArray());
+        }
+        @Override
+        public int hashCode() {
+            if (gloups == null) return 0;
+            return Arrays.deepHashCode(toArray());
+        }
+    }
+
+    // Need this to override equals.
+    public static class BandicootNotification extends Notification {
+        private static final long serialVersionUID = 664758643764049001L;
+        public BandicootNotification(String type, Object source, long seq) {
+            super(type,source,seq,0L,"");
+        }
+        private Object[] toArray() {
+            final Object[] vals = {getMessage(),getSequenceNumber(),
+                getSource(),getTimeStamp(),getType(),getUserData()};
+            return vals;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof BandicootNotification)) return false;
+            return Arrays.deepEquals(toArray(),
+                    ((BandicootNotification)o).toArray());
+        }
+        @Override
+        public int hashCode() {
+            return Arrays.deepHashCode(toArray());
+        }
+
+    }
+
+    // Need this to override equals.
+    public static class BandicootAttributeChangeNotification
+            extends AttributeChangeNotification {
+        private static final long serialVersionUID = -1392435607144396125L;
+        public BandicootAttributeChangeNotification(Object source,
+                long seq, long time, String msg, String name, String type,
+                Object oldv, Object newv) {
+            super(source,seq,time,msg,name,type,oldv,newv);
+        }
+        private Object[] toArray() {
+            final Object[] vals = {getMessage(),getSequenceNumber(),
+                getSource(),getTimeStamp(),getType(),getUserData(),
+                getAttributeName(), getAttributeType(),getNewValue(),
+                getOldValue()};
+            return vals;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof BandicootAttributeChangeNotification))
+                return false;
+            return Arrays.deepEquals(toArray(),
+                    ((BandicootAttributeChangeNotification)o).toArray());
+        }
+        @Override
+        public int hashCode() {
+            return Arrays.deepHashCode(toArray());
+        }
+        @Override
+        public String toString() {
+            final StringBuilder b = new StringBuilder();
+            b.append(this.getClass().getName()).append(": ");
+            b.append("[type=").append(getType()).append("]");
+            b.append("[source=").append(getSource()).append("]");
+            b.append("[message=").append(getMessage()).append("]");
+            b.append("[sequence=").append(getSequenceNumber()).append("]");
+
+            b.append("[attribute=").append(getAttributeName()).append("]");
+            b.append("[class=").append(getAttributeType()).append("]");
+            b.append("[oldvalue=").append(getOldValue()).append("]");
+            b.append("[newvalue=").append(getNewValue()).append("]");
+
+            b.append("[time=").append(getTimeStamp()).append("]");
+            b.append("[data=").append(getUserData()).append("]");
+            return b.toString();
+        }
+    }
+
+    private static void addToList(Object[] foos, List<Object> foolist) {
+        final ArrayList<Object> fal = new ArrayList<Object>(foos.length);
+        for (Object f : foos) {
+            if (f.getClass().isArray()) {
+                foolist.add(new BandicootClass(f));
+                fal.add(new BandicootClass(f));
+            } else {
+                foolist.add(f);
+                fal.add(f);
+            }
+        }
+        foolist.add(new BandicootClass(foos));
+        foolist.add(fal);
+    }
+
+    public static void testSerial(String msg, Object foo, Object bar,
+            RewritingProcessor procForFoo,
+            RewritingProcessor procForBar, List<Object> foolist,
+            List<Object> barlist, boolean recurse) {
+        System.err.println(msg+" Testing serial - "+foo.getClass().getName());
+        final Object bar1  = procForFoo.rewriteInput(foo);
+        final Object foo1  = procForFoo.rewriteOutput(bar);
+        final Object bar2  = procForFoo.rewriteInput(foo1);
+        final Object foo2  = procForFoo.rewriteOutput(bar1);
+
+        final Object bar3  = procForBar.rewriteOutput(foo);
+        final Object foo3  = procForBar.rewriteInput(bar);
+        final Object bar4  = procForBar.rewriteOutput(foo3);
+        final Object foo4  = procForBar.rewriteInput(bar3);
+
+        final Object bar5  = procForFoo.rewriteInput(foo3);
+        final Object foo5  = procForFoo.rewriteOutput(bar3);
+
+        final Object bar6  = procForBar.rewriteOutput(foo1);
+        final Object foo6  = procForBar.rewriteInput(bar1);
+
+        final Object[] foos = {foo, foo1, foo2, foo3, foo4, foo5, foo6};
+        final Object[] bars = {bar, bar1, bar2, bar3, bar4, bar5, bar6};
+
+        final Object[] foot = { foo };
+        final Object[] bart = { bar };
+        for (int j=1;j<foos.length;j++) {
+            final Object[] foox = { foos[j] };
+            final Object[] barx = { bars[j] };
+            if (!Arrays.deepEquals(foot,foox)) {
+                System.err.println(msg+" foo"+j+" "+foos[j]+" != "+foo);
+                throw new RuntimeException(msg+" foo"+j+" != foo");
+            }
+            if (!Arrays.deepEquals(bart,barx)) {
+                System.err.println(msg+" bar"+j+" "+bars[j]+" != "+bar);
+                throw new RuntimeException(msg+" bar"+j+" != bar");
+            }
+
+        }
+        if (recurse) {
+            testSerial("Array: " + msg,foos,bars,procForFoo,
+                    procForBar,foolist,barlist,false);
+            addToList(foos,foolist);
+            addToList(bars,barlist);
+        }
+    }
+    public static void testSerial(Object[][] objects,
+            RewritingProcessor procForFoo,
+            RewritingProcessor procForBar) {
+        int i=0;
+        final List<Object> foolist  = new LinkedList<Object>();
+        final List<Object> barlist = new LinkedList<Object>();
+        for (Object[] row : objects) {
+            i++;
+            Object foo = row[0];
+            Object bar = row[1];
+            String msg1 = "[" +foo.getClass().getName() + "] step " +
+                    i +": ";
+
+            testSerial(msg1,foo,bar,procForFoo,procForBar,foolist,barlist,true);
+
+            final BandicootClass kfoo = new BandicootClass(foo);
+            final BandicootClass kbar = new BandicootClass(bar);
+
+            String msg2 = "[" +kfoo.getClass().getName() + "] step " +
+                    i +": ";
+            testSerial(msg2,kfoo,kbar,procForFoo,procForBar,foolist,barlist,true);
+        }
+        String msg31 = "foo[] and bar[]: ";
+        testSerial(msg31,foolist.toArray(),barlist.toArray(),
+                   procForFoo,procForBar,foolist,barlist,false);
+
+        String msg3 = "foolist and barlist: ";
+        testSerial(msg3,new LinkedList<Object>(foolist),
+                   new LinkedList<Object>(barlist),
+                   procForFoo,procForBar,foolist,barlist,false);
+
+        final BandicootClass kfoolist = new BandicootClass(foolist);
+        final BandicootClass kbarlist = new BandicootClass(barlist);
+        String msg4 = "kfoolist and kbarlist: ";
+        testSerial(msg4,kfoolist,kbarlist,procForFoo,procForBar,foolist,barlist,false);
+    }
+
+    /**
+     * The idea of this  method is to convert {@code foo} things into
+     * {@code bar} things...
+     * @param foo the string to replace.
+     * @param bar the replacement for {@code foo}
+     *        ({@code foo} becomes {@code bar}).
+     * @param sfoo a string that may contain {@code foo}, that will be embedded
+     *        in non-replaceable parts of the domain in order to attempt to
+     *        trick the replacement logic.
+     * @param sbar a string that may contain {@code bar}, that will be embedded
+     *        in non-replaceable parts of the domain in order to attempt to
+     *        trick the replacement logic.
+     **/
+    public static void doSerialTest(String foo, String bar, String sfoo,
+                               String sbar) {
+        try {
+        final RewritingProcessor procForFoo = RewritingProcessor.
+                newRewritingProcessor(foo,bar);
+        final RewritingProcessor procForBar =RewritingProcessor.
+                newRewritingProcessor(bar,foo);
+        final String foop = (foo.isEmpty())?foo:foo+"//";
+        final String pfoo = (foo.isEmpty())?foo:"//"+foo;
+        final String barp = (bar.isEmpty())?bar:bar+"//";
+        final String pbar = (bar.isEmpty())?bar:"//"+bar;
+        final String sfoop = (sfoo.isEmpty())?sfoo:sfoo+"//";
+        final String psfoo = (sfoo.isEmpty())?sfoo:"//"+sfoo;
+        final String sbarp = (sbar.isEmpty())?sbar:sbar+"//";
+        final String psbar = (sbar.isEmpty())?sbar:"//"+sbar;
+
+        // A trick to avoid writing Open Data by hand...
+        final My tricks = new My();
+
+        // A treat to automagically convert trick things into Open Data.
+        final StandardMBean treats =
+                new StandardMBean(tricks,MyMXBean.class,true);
+
+        // datas[i][0] is expected to be transformed in datas[i][1]
+        //
+        final MyCompositeData[][] datas = {
+            { // this foo thing:
+            new MyCompositeData(new ObjectName(foop+sbarp+"x:y=z"),
+                    new ObjectName("//"+foop+sbarp+"x:y=z"),1,sfoop+sbarp+"foobar"),
+              // should be transformed into this bar thing:
+            new MyCompositeData(new ObjectName(barp+sbarp+"x:y=z"),
+                    new ObjectName("//"+foop+sbarp+"x:y=z"),1,sfoop+sbarp+"foobar"),
+            },
+            { // this foo thing:
+            new MyCompositeData(new ObjectName(foop+sfoop+"x:y=z"),
+                    new ObjectName("//"+foop+sfoop+"x:y=z"),1,sfoop+sbarp+"barfoo"),
+              // should be transformed into this bar thing:
+            new MyCompositeData(new ObjectName(barp+sfoop+"x:y=z"),
+                    new ObjectName("//"+foop+sfoop+"x:y=z"),1,sfoop+sbarp+"barfoo"),
+            }
+        };
+
+        // objects[i][0] is expected to be transformed into objects[i][1]
+        //
+        final Object[][] objects = new Object[][] {
+            {new Long(1), new Long(1)},
+            {
+                new ObjectName(foop+sbarp+"x:y=z"),
+                        new ObjectName(barp+sbarp+"x:y=z")
+            },
+            {
+                new ObjectName(foop+sfoop+"x:y=z"),
+                        new ObjectName(barp+sfoop+"x:y=z")
+            },
+            {
+                new ObjectName("//"+foop+sbarp+"x:y=z"),
+                new ObjectName("//"+foop+sbarp+"x:y=z"),
+            },
+            {
+                new ObjectName("//"+foop+sfoop+"x:y=z"),
+                new ObjectName("//"+foop+sfoop+"x:y=z")
+            },
+            {
+                foop+sbarp+"x:y=z",foop+sbarp+"x:y=z"
+            },
+            {
+                foop+sfoop+"x:y=z",foop+sfoop+"x:y=z"
+            },
+            {
+                barp+sbarp+"x:y=z",barp+sbarp+"x:y=z"
+            },
+            {
+                barp+sfoop+"x:y=z",barp+sfoop+"x:y=z"
+            },
+            {
+            new BandicootNotification("test",new ObjectName(foop+sfoop+"x:y=z"),1L),
+            new BandicootNotification("test",new ObjectName(barp+sfoop+"x:y=z"),1L),
+            },
+            {
+            new BandicootNotification("test",new ObjectName("//"+foop+sfoop+"x:y=z"),2L),
+            new BandicootNotification("test",new ObjectName("//"+foop+sfoop+"x:y=z"),2L),
+            },
+            {
+            new BandicootAttributeChangeNotification(
+                    new ObjectName(foop+sfoop+"x:y=z"),1L,2L,"blah","attrname",
+                    ObjectName.class.getName(),
+                    new ObjectName(foop+sfoop+"x:y=old"),
+                    new ObjectName(foop+sfoop+"x:y=new")),
+            new BandicootAttributeChangeNotification(
+                    new ObjectName(barp+sfoop+"x:y=z"),1L,2L,"blah","attrname",
+                    ObjectName.class.getName(),
+                    new ObjectName(barp+sfoop+"x:y=old"),
+                    new ObjectName(barp+sfoop+"x:y=new")),
+            },
+            {
+            new BandicootAttributeChangeNotification(
+                    new ObjectName("//"+foop+sfoop+"x:y=z"),1L,2L,"blah","attrname",
+                    ObjectName.class.getName(),
+                    new ObjectName("//"+foop+sfoop+"x:y=old"),
+                    new ObjectName(foop+sfoop+"x:y=new")),
+            new BandicootAttributeChangeNotification(
+                    new ObjectName("//"+foop+sfoop+"x:y=z"),1L,2L,"blah","attrname",
+                    ObjectName.class.getName(),
+                    new ObjectName("//"+foop+sfoop+"x:y=old"),
+                    new ObjectName(barp+sfoop+"x:y=new")),
+            }
+        };
+
+        // List that will merge datas & objects & datas converted to open
+        // types...
+        //
+        final List<Object[]> list = new ArrayList<Object[]>();
+
+        // Add all objects...
+        //
+        list.addAll(Arrays.asList(objects));
+
+        // Build Map<String,MyCompositeData> with datas[i][0] (cfoo)
+        //
+        for (int i=0;i<datas.length;i++) {
+            tricks.put(sfoop+sbarp+"x"+i,datas[i][0]);
+        }
+
+        // Let MXBean convert Map<String,MyCompositeData> to TabularData
+        // (foo things)
+        final Object cfoo = treats.getAttribute("All");
+        final AttributeList afoo = treats.getAttributes(new String[] {"All"});
+
+        // Build Map<String,MyCompositeData> with datas[i][1] (cbar)
+        //
+        for (int i=0;i<datas.length;i++) {
+            tricks.remove(sfoop+sbarp+"x"+i);
+            tricks.put(sfoop+sbarp+"x"+i,datas[i][1]);
+        }
+
+        // Let MXBean convert Map<String,MyCompositeData> to TabularData
+        // (bar things)
+        final Object cbar = treats.getAttribute("All");
+        final AttributeList abar = treats.getAttributes(new String[] {"All"});
+
+        // Add all datas to list
+        for (int i=0;i<datas.length;i++) {
+            list.add(datas[i]);
+        }
+
+        // Add converted TabularDatas to list
+        list.add(new Object[] {cfoo,cbar});
+
+        // Add AttributeList containing TabularData to list
+        list.add(new Object[] {afoo,abar});
+
+        // Add Arrays of the above to list...
+        list.add(new Object[] {new Object[] {cfoo,afoo,1L},
+                               new Object[] {cbar,abar,1L}});
+
+        // Add MBeanInfo...
+        list.add(new Object[] {treats.getMBeanInfo(),treats.getMBeanInfo()});
+
+        // No ready to test conversion of all foo things into bar things.
+        //
+        testSerial(list.toArray(new Object[list.size()][]),
+                procForFoo,procForBar);
+        } catch (JMException x) {
+            throw new RuntimeException(x);
+        }
+    }
+
+    public static void aaaTest() {
+        System.err.println("\n--------------------- aaaTest ----------------");
+        System.err.println("---------------- 'foo' becomes 'bar' ---------\n");
+        doSerialTest("foo","bar","foo","bar");
+    }
+
+    public static void aabTest() {
+        System.err.println("\n--------------------- aabTest ----------------");
+        System.err.println("---------- 'foo//bar' becomes 'bar//foo' -----\n");
+        doSerialTest("foo//bar","bar//foo","foo","bar");
+    }
+
+    public static void aacTest() {
+        System.err.println("\n----------------- aacTest --------------------");
+        System.err.println("------------ 'foo//bar' becomes '' -----------\n");
+        doSerialTest("foo//bar","","foo","bar");
+    }
+
+    public static void aadTest() {
+        System.err.println("\n----------------- aadTest --------------------");
+        System.err.println("----------- '' becomes 'bar//foo' ------------\n");
+        doSerialTest("","bar//foo","","bar//foo");
+    }
+
+    public static void aaeTest() {
+        System.err.println("\n----------------- aaeTest --------------------");
+        System.err.println("----------------- '' becomes '' --------------\n");
+        doSerialTest("","","foo","bar//foo");
+    }
+
+    // Let's be wild...
+    public static void aafTest() {
+        System.err.println("\n----------------- aafTest --------------------");
+        System.err.println("----------- '' becomes '' -- (bis) -----------\n");
+        doSerialTest("","","","");
+    }
+    public static void aagTest() {
+        System.err.println("\n----------------- aagTest --------------------");
+        System.err.println("----------- foobar becomes foobar ------------\n");
+        doSerialTest("foobar","foobar","foobar","foobar");
+    }
+
+    // TODO add test with descriptor, MBeanInfo, Open Types, etc...
+    public static void main(String[] args) {
+        aaaTest();
+        aabTest();
+        aacTest();
+        aadTest();
+        aaeTest();
+        aafTest();
+        aagTest();
+
+        // TODO: add a test case to test *exactly* the serialization
+        // of Notification and AttributeChangeNotification, and not of
+        // a subclass of these.
+        // This will involve implementing some hack, because we
+        // can't use equals() to compare the results.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/SourceNamespaceTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+/*
+ *
+ * @test SourceNamespaceTest.java
+ * @summary Test how queryNames works with Namespaces.
+ * @author Daniel Fuchs
+ * @run clean SourceNamespaceTest Wombat WombatMBean
+ * @run build SourceNamespaceTest Wombat WombatMBean
+ * @run main SourceNamespaceTest
+ */
+
+
+import javax.management.JMException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+
+/**
+ * A simple test to test the source directory parameter...
+ * @author dfuchs
+ */
+public class SourceNamespaceTest {
+
+
+    public static void localTest() throws JMException {
+        final JMXNamespace adir =
+                new JMXNamespace(MBeanServerFactory.newMBeanServer());
+
+        // put a wombat in adir...
+        final Wombat w1 = new Wombat();
+        final ObjectName wn1 = new ObjectName("wilderness:type=Wombat,name=gloups");
+        adir.getSourceServer().registerMBean(w1,wn1);
+
+        // register adir
+        final MBeanServer server = MBeanServerFactory.newMBeanServer();
+        server.registerMBean(adir, JMXNamespaces.getNamespaceObjectName("adir"));
+
+        if (! (server.isRegistered(JMXNamespaces.insertPath("adir", wn1))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("adir", wn1) + " not found");
+
+        System.out.println("Wombat gloups correctly registered...");
+
+        // put another wombat in adir...
+        final Wombat w2 = new Wombat();
+        final ObjectName wn2 =
+                new ObjectName("wilderness:type=Wombat,name=pasgloups");
+        server.registerMBean(w2,JMXNamespaces.insertPath("adir", wn2));
+
+        if (! (server.isRegistered(JMXNamespaces.insertPath("adir", wn2))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("adir", wn2) + " not found");
+
+        System.out.println("Wombat pasgloups correctly registered...");
+
+
+        // make an alias
+        final JMXNamespace alias = new JMXNamespace(
+                JMXNamespaces.narrowToNamespace(server,"adir"));
+        server.registerMBean(alias,
+                JMXNamespaces.getNamespaceObjectName("alias"));
+
+        if (! (server.isRegistered(JMXNamespaces.insertPath("alias", wn1))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("alias", wn1) + " not found");
+
+        System.out.println("Wombat gloups accessible through alias...");
+
+        if (! (server.isRegistered(JMXNamespaces.insertPath("alias", wn2))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("alias", wn2) + " not found");
+
+        System.out.println("Wombat pasgloups accessible through alias...");
+
+        final WombatMBean wp2 = JMX.newMBeanProxy(server,
+                JMXNamespaces.insertPath("alias",wn2), WombatMBean.class);
+        System.out.println(JMXNamespaces.insertPath("alias",wn2).toString()
+                +" says: "+wp2.getCaption());
+
+        // We're going to make another alias, but register it in a different
+        // MBeanServer. This is to make sure that source server and target
+        // server are not mixed up.
+        //
+        final MBeanServer server2 = MBeanServerFactory.newMBeanServer();
+        final JMXNamespace alias2 = new JMXNamespace(
+                JMXNamespaces.narrowToNamespace(server,"adir"));
+        server2.registerMBean(alias2,
+                JMXNamespaces.getNamespaceObjectName("alias2"));
+
+
+        if (! (server2.isRegistered(JMXNamespaces.insertPath("alias2", wn1))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("alias2", wn1) + " not found");
+
+        System.out.println("Wombat gloups accessible through alias2...");
+
+        if (! (server2.isRegistered(JMXNamespaces.insertPath("alias2", wn2))))
+            throw new RuntimeException("Test failed: " +
+                    JMXNamespaces.insertPath("alias2", wn2) + " not found");
+
+        System.out.println("Wombat pasgloups accessible through alias...");
+
+        final WombatMBean wp22 = JMX.newMBeanProxy(server2,
+                JMXNamespaces.insertPath("alias2",wn2), WombatMBean.class);
+        System.out.println(JMXNamespaces.insertPath("alias2",wn2).toString()
+                +" says: "+wp22.getCaption());
+
+
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        localTest();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/VirtualMBeanNotifTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,568 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test VirtualMBeanNotifTest.java
+ * @bug 5108776
+ * @build VirtualMBeanNotifTest Wombat WombatMBean
+ * @summary Test that Virtual MBeans can be implemented and emit notifs.
+ * @author  Daniel Fuchs
+ */
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import javax.management.Attribute;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+import javax.management.StandardMBean;
+import javax.management.event.EventSubscriber;
+import javax.management.namespace.VirtualEventManager;
+import javax.management.namespace.MBeanServerSupport;
+
+public class VirtualMBeanNotifTest {
+
+    /**
+     * An invocation handler that can implement DynamicMBean,
+     * NotificationBroadcaster, NotificationEmitter.
+     * The invocation handler works by forwarding all calls received from
+     * the implemented interfaces through a wrapped MBeanServer object.
+     */
+    public static class DynamicWrapper
+            implements InvocationHandler {
+
+        /**
+         * Inserts an additional class at the head of a signature array.
+         * @param first The first class in the signature
+         * @param rest  The other classes in the signature
+         * @return A signature array, of length rest.length+1.
+         */
+        static Class[] concat(Class first, Class... rest) {
+            if (rest == null || rest.length == 0) {
+                return new Class[] { first };
+            }
+            final Class[] sig = new Class[rest.length+1];
+            sig[0] = first;
+            System.arraycopy(rest, 0, sig, 1, rest.length);
+            return sig;
+        }
+
+        /**
+         * Inserts an additional object at the head of a parameters array.
+         * @param first The first object in the parameter array.
+         * @param rest  The other objects in the parameter array.
+         * @return A parameter array, of length rest.length+1.
+         */
+        static Object[] concat(Object first, Object... rest) {
+            if (rest == null || rest.length == 0) {
+                return new Object[] { first };
+            }
+            final Object[] params = new Object[rest.length+1];
+            params[0] = first;
+            System.arraycopy(rest, 0, params, 1, rest.length);
+            return params;
+        }
+
+        /**
+         * These two sets are used to check that all methods from
+         * implemented interfaces are mapped.
+         * unmapped is the set of methods that couldn't be mapped.
+         * mapped is the set of methods that could be mapped.
+         */
+        final static Set<Method> unmapped = new HashSet<Method>();
+        final static Set<Method> mapped = new HashSet<Method>();
+
+        /**
+         * For each method define in one of the interfaces intf, tries
+         * to find a corresponding method in the reference class ref, where
+         * the method in ref has the same name, and takes an additional
+         * ObjectName as first parameter.
+         *
+         * So for instance, if ref is MBeanServer and intf is {DynamicMBean}
+         * the result map is:
+         *     DynamicMBean.getAttribute -> MBeanServer.getAttribute
+         *     DynamicMBean.setAttribute -> MBeanServer.setAttribute
+         *     etc...
+         * If a method was mapped, it is additionally added to 'mapped'
+         * If a method couldn't be mapped, it is added to 'unmmapped'.
+         * In our example above, DynamicMBean.getNotificationInfo will end
+         * up in 'unmapped'.
+         *
+         * @param ref   The reference class - to which calls will be forwarded
+         *              with an additional ObjectName parameter inserted.
+         * @param intf  The proxy interface classes - for which we must find an
+         *              equivalent in 'ref'
+         * @return A map mapping the methods from intfs to the method of ref.
+         */
+        static Map<Method,Method> makeMapFor(Class<?> ref, Class<?>... intf) {
+            final Map<Method,Method> map = new HashMap<Method,Method>();
+            for (Class<?> clazz : intf) {
+                for (Method m : clazz.getMethods()) {
+                    try {
+                        final Method m2 =
+                            ref.getMethod(m.getName(),
+                            concat(ObjectName.class,m.getParameterTypes()));
+                        map.put(m,m2);
+                        mapped.add(m);
+                    } catch (Exception x) {
+                        unmapped.add(m);
+                    }
+                }
+            }
+            return map;
+        }
+
+        /**
+         * Tries to map all methods from DynamicMBean.class and
+         * NotificationEmitter.class to their equivalent in MBeanServer.
+         * This should be all the methods except
+         * DynamicMBean.getNotificationInfo.
+         */
+        static final Map<Method,Method> mbeanmap =
+                makeMapFor(MBeanServer.class,DynamicMBean.class,
+                NotificationEmitter.class);
+        /**
+         * Tries to map all methods from DynamicMBean.class and
+         * NotificationEmitter.class to an equivalent in DynamicWrapper.
+         * This time only DynamicMBean.getNotificationInfo will be mapped.
+         */
+        static final Map<Method,Method> selfmap =
+                makeMapFor(DynamicWrapper.class,DynamicMBean.class,
+                NotificationEmitter.class);
+
+        /**
+         * Now check that we have mapped all methods.
+         */
+        static {
+            unmapped.removeAll(mapped);
+            if (unmapped.size() > 0)
+                throw new ExceptionInInitializerError("Couldn't map "+ unmapped);
+        }
+
+        /**
+         * The wrapped MBeanServer to which everything is delegated.
+         */
+        private final MBeanServer server;
+
+        /**
+         * The name of the MBean we're proxying.
+         */
+        private final ObjectName name;
+        DynamicWrapper(MBeanServer server, ObjectName name) {
+            this.server=server;
+            this.name=name;
+        }
+
+        /**
+         * Creates a new proxy for the given MBean. Implements
+         * NotificationEmitter/NotificationBroadcaster if the proxied
+         * MBean also does.
+         * @param name    the name of the proxied MBean
+         * @param server  the wrapped server
+         * @return a DynamicMBean proxy
+         * @throws javax.management.InstanceNotFoundException
+         */
+        public static DynamicMBean newProxy(ObjectName name, MBeanServer server)
+            throws InstanceNotFoundException {
+            if (server.isInstanceOf(name,
+                    NotificationEmitter.class.getName())) {
+                // implements NotificationEmitter
+                return (DynamicMBean)
+                        Proxy.newProxyInstance(
+                        DynamicWrapper.class.getClassLoader(),
+                        new Class[] {NotificationEmitter.class,
+                        DynamicMBean.class},
+                        new DynamicWrapper(server, name));
+            }
+            if (server.isInstanceOf(name,
+                    NotificationBroadcaster.class.getName())) {
+                // implements NotificationBroadcaster
+                return (DynamicMBean)
+                        Proxy.newProxyInstance(
+                        DynamicWrapper.class.getClassLoader(),
+                        new Class[] {NotificationBroadcaster.class,
+                        DynamicMBean.class},
+                        new DynamicWrapper(server, name));
+            }
+            // Only implements DynamicMBean.
+            return (DynamicMBean)
+                        Proxy.newProxyInstance(
+                        DynamicWrapper.class.getClassLoader(),
+                        new Class[] {DynamicMBean.class},
+                        new DynamicWrapper(server, name));
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+            // Look for a method on this class (takes precedence)
+            final Method self = selfmap.get(method);
+            if (self != null)
+                return call(this,self,concat(name,args));
+
+            // no method found on this class, look for the same method
+            // on the wrapped MBeanServer
+            final Method mbean = mbeanmap.get(method);
+            if (mbean != null)
+                return call(server,mbean,concat(name,args));
+
+            // This isn't a method that can be forwarded to MBeanServer.
+            // If it's a method from Object, call it on this.
+            if (method.getDeclaringClass().equals(Object.class))
+                return call(this,method,args);
+            throw new NoSuchMethodException(method.getName());
+        }
+
+        // Call a method using reflection, unwraps invocation target exceptions
+        public Object call(Object handle, Method m, Object[] args)
+                throws Throwable {
+            try {
+                return m.invoke(handle, args);
+            } catch (InvocationTargetException x) {
+               throw x.getCause();
+            }
+        }
+
+        // this method is called when DynamicMBean.getNotificationInfo() is
+        // called. This is the method that should be mapped in
+        // 'selfmap'
+        public MBeanNotificationInfo[] getNotificationInfo(ObjectName name)
+            throws JMException {
+            return server.getMBeanInfo(name).getNotifications();
+        }
+    }
+
+    /**
+     * Just so that we can call the same test twice but with two
+     * different implementations of VirtualMBeanServerSupport.
+     */
+    public static interface MBeanServerWrapperFactory {
+        public MBeanServer wrapMBeanServer(MBeanServer wrapped);
+    }
+
+    /**
+     * A VirtualMBeanServerSupport that wrapps an MBeanServer and does not
+     * use VirtualEventManager.
+     */
+    public static class VirtualMBeanServerTest
+            extends MBeanServerSupport {
+
+        final MBeanServer wrapped;
+
+        public VirtualMBeanServerTest(MBeanServer wrapped) {
+            this.wrapped=wrapped;
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(final ObjectName name)
+                throws InstanceNotFoundException {
+            if (wrapped.isRegistered(name))
+                return DynamicWrapper.newProxy(name,wrapped);
+            throw new InstanceNotFoundException(String.valueOf(name));
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            return wrapped.queryNames(null, null);
+        }
+
+        public final static MBeanServerWrapperFactory factory =
+                new MBeanServerWrapperFactory() {
+
+            public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
+                return new VirtualMBeanServerTest(wrapped);
+            }
+            @Override
+            public String toString() {
+                return VirtualMBeanServerTest.class.getName();
+            }
+        };
+    }
+
+     /**
+     * A VirtualMBeanServerSupport that wrapps an MBeanServer and
+     * uses a VirtualEventManager.
+     */
+    public static class VirtualMBeanServerTest2
+            extends VirtualMBeanServerTest {
+
+        final EventSubscriber sub;
+        final NotificationListener nl;
+        final VirtualEventManager  mgr;
+
+        /**
+         * We use an EventSubscriber to subscribe for all notifications from
+         * the wrapped MBeanServer, and publish them through a
+         * VirtualEventManager. Not a very efficient way of doing things.
+         * @param wrapped
+         */
+        public VirtualMBeanServerTest2(MBeanServer wrapped) {
+            super(wrapped);
+            this.sub = EventSubscriber.getEventSubscriber(wrapped);
+            this.mgr = new VirtualEventManager();
+            this.nl = new NotificationListener() {
+                public void handleNotification(Notification notification, Object handback) {
+                    mgr.publish((ObjectName)notification.getSource(), notification);
+                }
+            };
+            try {
+                sub.subscribe(ObjectName.WILDCARD, nl, null, null);
+            } catch (RuntimeException x) {
+                throw x;
+            } catch (Exception x) {
+                throw new IllegalStateException("can't subscribe for notifications!");
+            }
+        }
+
+        @Override
+        public NotificationEmitter
+                getNotificationEmitterFor(ObjectName name)
+                throws InstanceNotFoundException {
+            final DynamicMBean mbean = getDynamicMBeanFor(name);
+            if (mbean instanceof NotificationEmitter)
+                return mgr.getNotificationEmitterFor(name);
+            return null;
+        }
+
+        public final static MBeanServerWrapperFactory factory =
+                new MBeanServerWrapperFactory() {
+
+            public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
+                return new VirtualMBeanServerTest2(wrapped);
+            }
+            @Override
+            public String toString() {
+                return VirtualMBeanServerTest2.class.getName();
+            }
+        };
+    }
+
+
+    public static void test(MBeanServerWrapperFactory factory) throws Exception {
+        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+
+        // names[] are NotificationEmitters
+        final ObjectName[] emitters = new ObjectName[2];
+        // shields[] have been shielded by wrapping them in a StandardMBean,
+        // so although the resource is an MBean that implements
+        // NotificationEmitter, the registered MBean (the wrapper) doesn't.
+        final ObjectName[] shielded = new ObjectName[2];
+
+        final List<ObjectName> registered = new ArrayList<ObjectName>(4);
+
+        try {
+            // register two MBeans before wrapping
+            server.registerMBean(new Wombat(),
+                    emitters[0] = new ObjectName("bush:type=Wombat,name=wom"));
+            registered.add(emitters[0]);
+
+            // we shield the second MBean in a StandardMBean so that it does
+            // not appear as a NotificationEmitter.
+            server.registerMBean(
+                    new StandardMBean(new Wombat(), WombatMBean.class),
+                    shielded[0] = new ObjectName("bush:type=Wombat,name=womshield"));
+            registered.add(shielded[0]);
+
+            final MBeanServer vserver = factory.wrapMBeanServer(server);
+
+            // register two other MBeans after wrapping
+            server.registerMBean(new Wombat(),
+                    emitters[1] = new ObjectName("bush:type=Wombat,name=bat"));
+            registered.add(emitters[1]);
+
+            // we shield the second MBean in a StandardMBean so that it does
+            // not appear as a NotificationEmitter.
+            server.registerMBean(
+                    new StandardMBean(new Wombat(), WombatMBean.class),
+                    shielded[1] = new ObjectName("bush:type=Wombat,name=batshield"));
+            registered.add(shielded[1]);
+
+            // Call test with this config - we have two wombats who broadcast
+            // notifs (emitters) and two wombats who don't (shielded).
+            test(vserver, emitters, shielded);
+
+            System.out.println("*** Test passed for: " + factory);
+        } finally {
+            // Clean up the platform mbean server for the next test...
+            for (ObjectName n : registered) {
+                try {
+                    server.unregisterMBean(n);
+                } catch (Exception x) {
+                    x.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * Perform the actual test.
+     * @param vserver    A virtual MBeanServerSupport implementation
+     * @param emitters   Names of NotificationBroadcaster MBeans
+     * @param shielded   Names of non NotificationBroadcaster MBeans
+     * @throws java.lang.Exception
+     */
+    public static void test(MBeanServer vserver, ObjectName[] emitters,
+            ObjectName[] shielded) throws Exception {
+
+        // To catch exception in NotificationListener
+        final List<Exception> fail = new CopyOnWriteArrayList<Exception>();
+
+        // A queue of received notifications
+        final BlockingQueue<Notification> notifs =
+                new ArrayBlockingQueue<Notification>(50);
+
+        // A notification listener that puts the notification it receives
+        // in the queue.
+        final NotificationListener handler = new NotificationListener() {
+
+            public void handleNotification(Notification notification,
+                    Object handback) {
+                try {
+                    notifs.put(notification);
+                } catch (Exception x) {
+                    fail.add(x);
+                }
+            }
+        };
+
+        // A list of attribute names for which we might receive an
+        // exception. If an exception is received when getting these
+        // attributes - the test will not fail.
+        final List<String> exceptions = Arrays.asList( new String[] {
+           "UsageThresholdCount","UsageThreshold","UsageThresholdExceeded",
+           "CollectionUsageThresholdCount","CollectionUsageThreshold",
+           "CollectionUsageThresholdExceeded"
+        });
+
+        // This is just a sanity check. Get all attributes of all MBeans.
+        for (ObjectName n : vserver.queryNames(null, null)) {
+            final MBeanInfo m = vserver.getMBeanInfo(n);
+            for (MBeanAttributeInfo mba : m.getAttributes()) {
+                // System.out.println(n+":");
+                Object val;
+                try {
+                    val = vserver.getAttribute(n, mba.getName());
+                } catch (Exception x) {
+                    // only accept exception for those attributes that
+                    // have a valid reason to fail...
+                    if (exceptions.contains(mba.getName())) val = x;
+                    else throw new Exception("Failed to get " +
+                            mba.getName() + " from " + n,x);
+                }
+                // System.out.println("\t "+mba.getName()+": "+ val);
+            }
+        }
+
+        // The actual tests. Register for notifications with notif emitters
+        for (ObjectName n : emitters) {
+            vserver.addNotificationListener(n, handler, null, n);
+        }
+
+        // Trigger the emission of notifications, check that we received them.
+        for (ObjectName n : emitters) {
+            vserver.setAttribute(n,
+                    new Attribute("Caption","I am a new wombat!"));
+            final Notification notif = notifs.poll(4, TimeUnit.SECONDS);
+            if (!notif.getSource().equals(n))
+                throw new Exception("Bad source for "+ notif);
+            if (fail.size() > 0)
+                throw new Exception("Failed to handle notif",fail.remove(0));
+        }
+
+        // Check that we didn't get more notifs than expected
+        if (notifs.size() > 0)
+            throw new Exception("Extra notifications in queue: "+notifs);
+
+        // Check that if the MBean doesn't exist, we get InstanceNotFound.
+        try {
+            vserver.addNotificationListener(new ObjectName("toto:toto=toto"),
+                    handler, null, null);
+            throw new Exception("toto:toto=toto doesn't throw INFE");
+        } catch (InstanceNotFoundException x) {
+            System.out.println("Received "+x+" as expected.");
+        }
+
+        // For those MBeans that shouldn't be NotificationEmitters, check that
+        // we get IllegalArgumentException
+        for (ObjectName n : shielded) {
+            try {
+                vserver.addNotificationListener(n, handler, null, n);
+            } catch (RuntimeOperationsException x) {
+                System.out.println("Received "+x+" as expected.");
+                System.out.println("Cause is: "+x.getCause());
+                if (!(x.getCause() instanceof IllegalArgumentException))
+                    throw new Exception("was expecting IllegalArgumentException cause. Got "+x.getCause(),x);
+            }
+        }
+
+        // Sanity check. Remove our listeners.
+        for (ObjectName n : emitters) {
+            vserver.removeNotificationListener(n, handler, null, n);
+        }
+
+        // That's it.
+        // Sanity check: we shouldn't have received any new notif.
+        if (notifs.size() > 0)
+            throw new Exception("Extra notifications in queue: "+notifs);
+        // The NotifListener shouldn't have logged any new exception.
+        if (fail.size() > 0)
+                throw new Exception("Failed to handle notif",fail.remove(0));
+    }
+
+    public static void main(String[] args) throws Exception {
+        // test with a regular MBeanServer (no VirtualMBeanServerSupport)
+        final MBeanServerWrapperFactory identity =
+                new MBeanServerWrapperFactory() {
+            public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
+                return wrapped;
+            }
+        };
+        test(identity);
+        // test with no EventManager
+        test(VirtualMBeanServerTest.factory);
+        // test with VirtualEventManager
+        test(VirtualMBeanServerTest2.factory);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/VirtualMBeanTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,409 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test VirtualMBeanTest.java
+ * @bug 5108776
+ * @summary Test that Virtual MBeans can be implemented and emit notifs.
+ * @author Eamonn McManus
+ */
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+import javax.management.SendNotification;
+import javax.management.StandardEmitterMBean;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.VirtualEventManager;
+import javax.management.namespace.MBeanServerSupport;
+import javax.management.timer.TimerMBean;
+
+// In this test, we check that the two main use case types for
+// MBeanServerSupport work correctly:
+// (1) as a special-purpose implementation of MBeanServer for a fixed number
+//     of MBeans (e.g. for QueryNotificationFilter)
+// (2) as an MBeanServer supporting Virtual MBeans.
+// In each case we are particularly interested in the notification behaviour.
+// We check that the behaviour is correct when calling addNotificationListener
+// (a) for an MBean that does not exist; (b) for an MBean that does exist but
+// is not a NotificationEmitter; and (c) for an MBean that exists and is
+// a NotificationEmitter.  We also check the degenerate and usual case
+// where the MBeanServerSupport subclass does not support notifications
+// at all.
+//
+// Each subclass will have an MBean called test:type=NotEmitter that
+// does not support addNotificationListener. If it also has MBeans called
+// test:type=Emitter,* then they are expected to support addNL. No subclass
+// will have any other MBeans, so in particular no subclass will have
+// test:type=Nonexistent.
+//
+public class VirtualMBeanTest {
+    static final ObjectName
+            nonExistentName, notEmitterName, emitterName1, emitterName2;
+    static {
+        try {
+            nonExistentName = new ObjectName("test:type=NonExistent");
+            notEmitterName = new ObjectName("test:type=NotEmitter");
+            emitterName1 = new ObjectName("test:type=Emitter,id=1");
+            emitterName2 = new ObjectName("test:type=Emitter,id=2");
+        } catch (MalformedObjectNameException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    static final StandardMBean.Options wrappedVisible = new StandardMBean.Options();
+    static {
+        wrappedVisible.setWrappedObjectVisible(true);
+    }
+
+    public static interface NothingMBean {}
+    public static class Nothing implements NothingMBean {}
+    public static class NothingNBS extends NotificationBroadcasterSupport
+            implements NothingMBean {}
+
+    // Class that has hardwired MBeans test:type=NotEmitter,
+    // test:type=Broadcaster, and test:type=Emitter.
+    private static class HardwiredMBS extends MBeanServerSupport
+            implements SendNotification {
+        private final DynamicMBean notEmitter =
+                new StandardMBean(new Nothing(), NothingMBean.class, wrappedVisible);
+        private final StandardEmitterMBean emitter1, emitter2;
+        {
+            NothingNBS nnbs1 = new NothingNBS();
+            emitter1 = new StandardEmitterMBean(
+                    nnbs1, NothingMBean.class, wrappedVisible, nnbs1);
+            NothingNBS nnbs2 = new NothingNBS();
+            emitter2 = new StandardEmitterMBean(
+                    nnbs2, NothingMBean.class, wrappedVisible, nnbs2);
+        }
+
+        private final Map<ObjectName, DynamicMBean> map =
+                new TreeMap<ObjectName, DynamicMBean>();
+        {
+            map.put(notEmitterName, notEmitter);
+            map.put(emitterName1, emitter1);
+            map.put(emitterName2, emitter2);
+        }
+
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            DynamicMBean mbean = map.get(name);
+            if (mbean != null)
+                return mbean;
+            else
+                throw new InstanceNotFoundException(name);
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            return map.keySet();
+        }
+
+        @Override
+        public String toString() {
+            return "Hardwired MBeanServerSupport";
+        }
+
+        public void sendNotification(Notification notification) {
+            emitter1.sendNotification(notification);
+            emitter2.sendNotification(notification);
+        }
+    }
+
+    // Class that has the notEmitter MBean but not either of the others, so does
+    // not support listeners.
+    private static class VirtualMBSWithoutListeners
+            extends MBeanServerSupport {
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            if (name.equals(notEmitterName)) {
+                return new StandardMBean(
+                        new Nothing(), NothingMBean.class, wrappedVisible);
+            } else
+                throw new InstanceNotFoundException(name);
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            return Collections.singleton(notEmitterName);
+        }
+
+        @Override
+        public String toString() {
+            return "Virtual MBeanServerSupport without listener support";
+        }
+    }
+
+    // Class that has the notEmitter and emitter MBeans as Virtual MBeans, using
+    // VirtualEventManager to handle listeners for the emitter MBean.  We
+    // implement the broadcaster MBean (which is a NotificationBroadcaster but
+    // not a NotificationEmitter) even though it's very hard to imagine a real
+    // use case where that would happen.
+    private static class VirtualMBSWithListeners
+            extends MBeanServerSupport implements SendNotification {
+        private final VirtualEventManager vem = new VirtualEventManager();
+
+        private static final List<ObjectName> names =
+                Arrays.asList(notEmitterName, emitterName1, emitterName2);
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            if (names.contains(name)) {
+                return new StandardMBean(
+                        new Nothing(), NothingMBean.class, wrappedVisible);
+            } else
+                throw new InstanceNotFoundException(name);
+        }
+
+        @Override
+        public NotificationEmitter getNotificationEmitterFor(
+                ObjectName name) throws InstanceNotFoundException {
+            if (name.equals(emitterName1) || name.equals(emitterName2))
+                return vem.getNotificationEmitterFor(name);
+            else if (name.equals(notEmitterName))
+                return null;
+            else
+                throw new InstanceNotFoundException(name);
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            return new TreeSet<ObjectName>(Arrays.asList(notEmitterName, emitterName2));
+        }
+
+        @Override
+        public String toString() {
+            return "Virtual MBeanServerSupport with listener support";
+        }
+
+        public void sendNotification(Notification notification) {
+            vem.publish(emitterName1, notification);
+            vem.publish(emitterName2, notification);
+        }
+    }
+
+    private static final MBeanServer[] vmbsss = {
+        new HardwiredMBS(),
+        new VirtualMBSWithoutListeners(),
+        new VirtualMBSWithListeners(),
+    };
+
+    public static void main(String[] args) throws Exception {
+        Exception lastEx = null;
+        for (MBeanServer vmbs : vmbsss) {
+            String testName = "\"" + vmbs + "\"";
+            System.out.println("===Test " + testName + "===");
+            try {
+                test(vmbs);
+            } catch (Exception e) {
+                System.out.println(
+                        "===Test " + testName + " failed with exception " + e);
+                StringWriter sw = new StringWriter();
+                PrintWriter pw = new PrintWriter(sw);
+                e.printStackTrace(pw);
+                pw.flush();
+                String es = sw.toString();
+                System.out.println("......" + es.replace("\n", "\n......"));
+                lastEx = e;
+            }
+        }
+        if (lastEx != null)
+            throw lastEx;
+        System.out.println("TEST PASSED");
+    }
+
+    private static class NothingListener implements NotificationListener {
+        public void handleNotification(Notification notification,
+                                       Object handback) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    private static class QueueListener implements NotificationListener {
+        final BlockingQueue<Notification> queue =
+                new ArrayBlockingQueue<Notification>(10);
+
+        public void handleNotification(Notification notification,
+                                       Object handback) {
+            queue.add(notification);
+        }
+    }
+
+    private static void test(MBeanServer vmbs) throws Exception {
+        MBeanServer mmbs = MBeanServerFactory.newMBeanServer();
+        ObjectName namespaceName = new ObjectName("test//:type=JMXNamespace");
+        JMXNamespace namespace = new JMXNamespace(vmbs);
+        mmbs.registerMBean(namespace, namespaceName);
+        MBeanServer mbs = JMXNamespaces.narrowToNamespace(mmbs, "test");
+
+        Set<ObjectName> names = mbs.queryNames(null, null);
+        //names.remove(new ObjectName(":type=JMXNamespace"));
+
+        // Make sure that notEmitterName exists according to query...
+        System.out.println("Checking query");
+        if (!names.contains(notEmitterName))
+            throw new Exception("Bad query result: " + names);
+
+        // ...and according to getMBeanInfo
+        System.out.println("Checking getMBeanInfo(" + notEmitterName + ")");
+        MBeanInfo mbi = mbs.getMBeanInfo(notEmitterName);
+        if (mbi.getNotifications().length > 0)
+            throw new Exception("notEmitter has NotificationInfo");
+
+        // Make sure we get the right exception for getMBeanInfo on a
+        // non-existent MBean
+        System.out.println("Checking getMBeanInfo on a non-existent MBean");
+        try {
+            mbi = mbs.getMBeanInfo(nonExistentName);
+            throw new Exception("getMBI succeeded but should not have");
+        } catch (InstanceNotFoundException e) {
+        }
+
+        // Make sure we get the right exception for addNotificationListener on a
+        // non-existent MBean
+        System.out.println(
+                "Checking addNotificationListener on a non-existent MBean");
+        try {
+            mbs.addNotificationListener(
+                    nonExistentName, new NothingListener(), null, null);
+            throw new Exception("addNL succeeded but should not have");
+        } catch (InstanceNotFoundException e) {
+        }
+
+        // Make sure we get the right exception for isInstanceOf on a
+        // non-existent MBean
+        System.out.println(
+                "Checking isInstanceOf on a non-existent MBean");
+        for (Class<?> c : new Class<?>[] {
+            Object.class, NotificationBroadcaster.class, NotificationEmitter.class,
+        }) {
+            try {
+                boolean is = mbs.isInstanceOf(nonExistentName, c.getName());
+                throw new Exception(
+                        "isInstanceOf " + c.getName() +
+                        " succeeded but should not have");
+            } catch (InstanceNotFoundException e) {
+            }
+        }
+
+        // Make sure isInstanceOf works correctly for classes without special
+        // treatment
+        System.out.println(
+                "Checking isInstanceOf on normal classes");
+        for (ObjectName name : names) {
+            boolean isNothing = mbs.isInstanceOf(name, NothingMBean.class.getName());
+            if (!isNothing) {
+                throw new Exception("isInstanceOf " + NothingMBean.class.getName() +
+                        " returned false, should be true");
+            }
+            boolean isTimer = mbs.isInstanceOf(name, TimerMBean.class.getName());
+            if (isTimer) {
+                throw new Exception("isInstanceOf " + TimerMBean.class.getName() +
+                        " returned true, should be false");
+            }
+        }
+
+        // Make sure that addNL on notEmitterName gets the right exception
+        System.out.println("Checking addNL on non-broadcaster");
+        try {
+            mbs.addNotificationListener(
+                    notEmitterName, new NothingListener(), null, null);
+            throw new Exception("addNL succeeded but should not have");
+        } catch (RuntimeOperationsException e) {
+            if (!(e.getCause() instanceof IllegalArgumentException))
+                throw new Exception("Wrong exception from addNL", e);
+        }
+
+        if (!(vmbs instanceof SendNotification)) {
+            System.out.println("Not testing notifications for this implementation");
+            return;
+        }
+
+        QueueListener qListener = new QueueListener();
+
+        System.out.println("Testing addNL on emitters");
+        mbs.addNotificationListener(emitterName1, qListener, null, null);
+        mbs.addNotificationListener(emitterName2, qListener, null, null);
+
+        System.out.println("Testing that listeners work");
+        Notification notif = new Notification("notif.type", "source", 0L);
+
+        ((SendNotification) vmbs).sendNotification(notif);
+        testListeners(qListener, "notif.type", 2);
+
+        System.out.println("Testing 2-arg removeNL on emitter1");
+        mbs.removeNotificationListener(emitterName1, qListener);
+
+        ((SendNotification) vmbs).sendNotification(notif);
+        testListeners(qListener, "notif.type", 1);
+
+        System.out.println("Testing 4-arg removeNL on emitter2");
+        mbs.removeNotificationListener(emitterName2, qListener, null, null);
+
+        ((SendNotification) vmbs).sendNotification(notif);
+        testListeners(qListener, "notif.type", 0);
+    }
+
+    private static void testListeners(
+            QueueListener qListener, String expectedNotifType, int expectedNotifs)
+            throws Exception {
+        for (int i = 1; i <= expectedNotifs; i++) {
+            Notification rNotif = qListener.queue.poll(1, TimeUnit.SECONDS);
+            if (rNotif == null)
+                throw new Exception("Notification " + i + " never arrived");
+            if (!rNotif.getType().equals(expectedNotifType))
+                throw new Exception("Wrong type notif: " + rNotif.getType());
+        }
+        Notification xNotif = qListener.queue.poll(10, TimeUnit.MILLISECONDS);
+        if (xNotif != null)
+            throw new Exception("Extra notif: " + xNotif);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/VirtualNamespaceQueryTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ *
+ * @test VirtualNamespaceQueryTest.java
+ * @summary General VirtualNamespaceQueryTest test.
+ * @author Daniel Fuchs
+ * @run clean VirtualNamespaceQueryTest Wombat WombatMBean
+ *            NamespaceController NamespaceControllerMBean
+ *            JMXRemoteTargetNamespace
+ * @compile -XDignore.symbol.file=true VirtualNamespaceQueryTest.java
+ *          Wombat.java WombatMBean.java
+ *          NamespaceController.java NamespaceControllerMBean.java
+ *          JMXRemoteTargetNamespace.java
+ * @run main VirtualNamespaceQueryTest
+ */
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotificationEmitter;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.MBeanServerSupport;
+
+/**
+ *
+ * @author dfuchs
+ */
+public class VirtualNamespaceQueryTest {
+    public static class WombatRepository extends MBeanServerSupport {
+        final Wombat wombat;
+        final StandardMBean mbean;
+        final ObjectName wombatName;
+
+        public WombatRepository(ObjectName wombatName) {
+            try {
+                wombat = new Wombat();
+                mbean  = wombat;
+                this.wombatName = wombatName;
+                wombat.preRegister(null,wombatName);
+            } catch (Exception x) {
+                throw new IllegalArgumentException(x);
+            }
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+            throws InstanceNotFoundException {
+            if (wombatName.equals(name)) return mbean;
+            else throw new InstanceNotFoundException(String.valueOf(name));
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            final Set<ObjectName> res = Collections.singleton(wombatName);
+            return res;
+        }
+
+        @Override
+        public NotificationEmitter getNotificationEmitterFor(
+                ObjectName name) throws InstanceNotFoundException {
+            DynamicMBean mb = getDynamicMBeanFor(name);
+            if (mb instanceof NotificationEmitter)
+                return (NotificationEmitter)mb;
+            return null;
+        }
+    }
+    public static class WombatNamespace extends JMXNamespace {
+        public WombatNamespace(ObjectName wombatName) {
+            super(new WombatRepository(wombatName));
+        }
+    }
+
+    public static void simpleTest() throws Exception {
+        final MBeanServer  server = MBeanServerFactory.newMBeanServer();
+        final ObjectName   wombatName = new ObjectName("burrow:type=Wombat");
+        final JMXNamespace ns = new WombatNamespace(wombatName);
+        server.registerMBean(ns, JMXNamespaces.getNamespaceObjectName("wombats"));
+        final Set<ObjectName> dirs =
+                server.queryNames(new ObjectName("wombats//*//:type=JMXNamespace"),
+                wombatName);
+        System.out.println("all dirs: "+dirs);
+        if (dirs.size()>0)
+            throw new RuntimeException("Unexpected ObjectNames returned: "+dirs);
+
+        final ObjectInstance inst = NamespaceController.createInstance(server);
+        final NamespaceControllerMBean controller =
+                JMX.newMBeanProxy(server, inst.getObjectName(),
+                NamespaceControllerMBean.class);
+        final String[] dirNames = controller.findNamespaces(null,null,2);
+        System.err.println(Arrays.toString(dirNames));
+    }
+
+    public static void main(String[] args) throws Exception {
+        simpleTest();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/VirtualPropsTest.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 5108776
+ * @summary Test the properties use case for Virtual MBeans that is documented
+ * in MBeanServerSupport.
+ * @author Eamonn McManus
+ */
+
+import java.lang.management.ManagementFactory;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.VirtualEventManager;
+import javax.management.namespace.MBeanServerSupport;
+
+public class VirtualPropsTest {
+    public static interface PropertyMBean {
+        public String getValue();
+    }
+
+    public static class PropsMBS extends MBeanServerSupport {
+        private static ObjectName newObjectName(String name) {
+            try {
+                return new ObjectName(name);
+            } catch (MalformedObjectNameException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        public static class PropertyImpl implements PropertyMBean {
+            private final String name;
+
+            public PropertyImpl(String name) {
+                this.name = name;
+            }
+
+            public String getValue() {
+                return System.getProperty(name);
+            }
+        }
+
+        @Override
+        public DynamicMBean getDynamicMBeanFor(ObjectName name)
+                throws InstanceNotFoundException {
+            ObjectName namePattern = newObjectName(
+                        "com.example:type=Property,name=\"*\"");
+            if (!namePattern.apply(name))
+                throw new InstanceNotFoundException(name);
+
+            String propName = ObjectName.unquote(name.getKeyProperty("name"));
+            if (System.getProperty(propName) == null)
+                throw new InstanceNotFoundException(name);
+            PropertyMBean propMBean = new PropertyImpl(propName);
+            return new StandardMBean(propMBean, PropertyMBean.class, false);
+        }
+
+        @Override
+        protected Set<ObjectName> getNames() {
+            Set<ObjectName> names = new TreeSet<ObjectName>();
+            Properties props = System.getProperties();
+            for (String propName : props.stringPropertyNames()) {
+                ObjectName objectName = newObjectName(
+                        "com.example:type=Property,name=" +
+                        ObjectName.quote(propName));
+                names.add(objectName);
+            }
+            return names;
+        }
+
+        private final VirtualEventManager vem = new VirtualEventManager();
+
+        @Override
+        public NotificationEmitter getNotificationEmitterFor(
+                ObjectName name) throws InstanceNotFoundException {
+            getDynamicMBeanFor(name);  // check that the name is valid
+            return vem.getNotificationEmitterFor(name);
+        }
+
+        public void propertyChanged(String name, String newValue) {
+            ObjectName objectName = newObjectName(
+                    "com.example:type=Property,name=" + ObjectName.quote(name));
+            Notification n = new Notification(
+                    "com.example.property.changed", objectName, 0L,
+                    "Property " + name + " changed");
+            n.setUserData(newValue);
+            vem.publish(objectName, n);
+        }
+    }
+
+    static class QueueListener implements NotificationListener {
+        BlockingQueue<Notification> q = new ArrayBlockingQueue<Notification>(10);
+        public void handleNotification(Notification notification,
+                                       Object handback) {
+            q.add(notification);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        MBeanServer mmbs = ManagementFactory.getPlatformMBeanServer();
+        String namespace = "props";
+        PropsMBS pmbs = new PropsMBS();
+        Object namespaceMBean = new JMXNamespace(pmbs);
+        mmbs.registerMBean(namespaceMBean, new ObjectName(
+                namespace + "//:type=JMXNamespace"));
+        MBeanServer mbs = JMXNamespaces.narrowToNamespace(mmbs, namespace);
+
+        Properties props = System.getProperties();
+
+        int nprops = props.stringPropertyNames().size();
+        if (nprops != mbs.getMBeanCount()) {
+            throw new Exception(String.format("Properties: %d; MBeans: %d",
+                    nprops, mbs.getMBeanCount()));
+        }
+
+        for (String propName : props.stringPropertyNames()) {
+            ObjectName propObjectName = new ObjectName(
+                    "com.example:type=Property,name=" + ObjectName.quote(propName));
+            PropertyMBean propProx = JMX.newMBeanProxy(
+                    mbs, propObjectName, PropertyMBean.class);
+            String propValue = propProx.getValue();
+            String realPropValue = props.getProperty(propName);
+            if (!realPropValue.equals(propValue)) {
+                throw new Exception(String.format("Property %s: value is \"%s\"; " +
+                        "mbean says \"%s\"", propName, realPropValue, propValue));
+            }
+        }
+
+        ObjectName fooPropObjectName =
+                new ObjectName("com.example:type=Property,name=\"java.home\"");
+        QueueListener ql = new QueueListener();
+        mbs.addNotificationListener(fooPropObjectName, ql, null, null);
+        pmbs.propertyChanged("java.home", "bar");
+        Notification n = ql.q.poll(1, TimeUnit.SECONDS);
+        if (n == null)
+            throw new Exception("Notif didn't arrive");
+        if (!"bar".equals(n.getUserData()))
+            throw new Exception("Bad user data: " + n.getUserData());
+
+        System.out.println("TEST PASSED");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/Wombat.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.util.Random;
+import java.util.Set;
+import javax.management.AttributeChangeNotification;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+
+/**
+ * Dynamic MBean based on StandardMBean
+ * Class Wombat
+ * Wombat Description
+ * @author dfuchs
+ */
+public class Wombat extends StandardMBean
+        implements WombatMBean, NotificationEmitter, MBeanRegistration {
+
+    /**
+     * Attribute : Caption
+     */
+    private String caption = "I'm a wombat";
+
+    private final long MAX_SEED = 36000;
+    private final long seed;
+    private final long period;
+    private volatile int mood = 0;
+
+    public int getMood() {
+        final long  degree = seed + (System.currentTimeMillis()/period)%MAX_SEED;
+        final double angle = ((double)degree)/100;
+        mood = (int)(100.0*Math.sin(angle));
+        return mood;
+    }
+
+    public Wombat() throws NotCompliantMBeanException {
+        super(WombatMBean.class);
+        final Random r = new Random();
+        seed = ((r.nextLong() % MAX_SEED) + MAX_SEED)%MAX_SEED;
+        period = 200 + (((r.nextLong()%80)+80)%80)*10;
+    }
+
+    /**
+     * Next are the methods to compute MBeanInfo.
+     * You shouldn't update these methods.
+     */
+    @Override
+    protected String getDescription(MBeanInfo info) {
+        return "Wombats are strange beasts. You will find them down under " +
+                "and in some computer programms.";
+    }
+
+    @Override
+    protected String getDescription(MBeanAttributeInfo info) {
+        String description = null;
+        if (info.getName().equals("Caption")) {
+            description = "A simple caption to describe a wombat";
+        }
+        if (info.getName().equals("Mood")) {
+            description = "This Wombat's mood on a [-100,+100] scale."+
+                      " -100 means that this wombat is very angry.";
+        }
+        return description;
+    }
+
+    @Override
+    protected String getDescription(MBeanOperationInfo op,
+            MBeanParameterInfo param,
+            int sequence) {
+        return null;
+    }
+
+    @Override
+    protected String getParameterName(MBeanOperationInfo op,
+            MBeanParameterInfo param,
+            int sequence) {
+        return null;
+    }
+
+    @Override
+    protected String getDescription(MBeanOperationInfo info) {
+        String description = null;
+        return description;
+    }
+
+    @Override
+    public MBeanInfo getMBeanInfo() {
+        MBeanInfo mbinfo = super.getMBeanInfo();
+        return new MBeanInfo(mbinfo.getClassName(),
+                mbinfo.getDescription(),
+                mbinfo.getAttributes(),
+                mbinfo.getConstructors(),
+                mbinfo.getOperations(),
+                getNotificationInfo());
+    }
+
+    /**
+     * Get A simple caption to describe a wombat
+     */
+    public synchronized String getCaption() {
+        return caption;
+    }
+
+    /**
+     * Set A simple caption to describe a wombat
+     */
+    public void setCaption(String value) {
+        final String oldValue;
+        synchronized (this) {
+            oldValue = caption;
+            caption = value;
+        }
+        final AttributeChangeNotification notif =
+                new AttributeChangeNotification(objectName,
+                    getNextSeqNumber(),
+                    System.currentTimeMillis(),
+                    "Caption changed","Caption",
+                    String.class.getName(),oldValue,value);
+        broadcaster.sendNotification(notif);
+    }
+
+    /**
+     * MBeanNotification support
+     * You shouldn't update these methods
+     */
+    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
+        broadcaster.addNotificationListener(listener, filter, handback);
+    }
+
+    public MBeanNotificationInfo[] getNotificationInfo() {
+        return new MBeanNotificationInfo[] {
+            new MBeanNotificationInfo(new String[] {
+                AttributeChangeNotification.ATTRIBUTE_CHANGE},
+                javax.management.AttributeChangeNotification.class.getName(),
+                "Sent when the caption changes")
+            };
+    }
+
+    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener);
+    }
+
+    public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
+        broadcaster.removeNotificationListener(listener, filter, handback);
+    }
+
+    private synchronized long getNextSeqNumber() {
+        return seqNumber++;
+    }
+
+    private long seqNumber;
+
+    private final NotificationBroadcasterSupport broadcaster = new NotificationBroadcasterSupport();
+
+    /**
+     * Allows the MBean to perform any operations it needs before being
+     * registered in the MBean server. If the name of the MBean is not
+     * specified, the MBean can provide a name for its registration. If
+     * any exception is raised, the MBean will not be registered in the
+     * MBean server.
+     * @param server The MBean server in which the MBean will be registered.
+     * @param name The object name of the MBean. This name is null if the
+     * name parameter to one of the createMBean or registerMBean methods in
+     * the MBeanServer interface is null. In that case, this method must
+     * return a non-null ObjectName for the new MBean.
+     * @return The name under which the MBean is to be registered. This value
+     * must not be null. If the name parameter is not null, it will usually
+     * but not necessarily be the returned value.
+     * @throws Exception This exception will be caught by the MBean server and
+     * re-thrown as an MBeanRegistrationException.
+     */
+    @Override
+    public ObjectName preRegister(MBeanServer server, ObjectName name)
+            throws Exception {
+        objectName = name;
+        mbeanServer = server;
+        return super.preRegister(server, name);
+    }
+
+    /**
+     * Allows the MBean to perform any operations needed after having
+     * been registered in the MBean server or after the registration has
+     * failed.
+     * @param registrationDone Indicates wether or not the MBean has been
+     * successfully registered in the MBean server. The value false means
+     * that the registration has failed.
+     */
+    @Override
+    public void postRegister(Boolean registrationDone) {
+        super.postRegister(registrationDone);
+    }
+
+    /**
+     * Allows the MBean to perform any operations it needs before being
+     * unregistered by the MBean server.
+     * @throws Exception This exception will be caught by the MBean server and
+     * re-thrown as an MBeanRegistrationException.
+     */
+    @Override
+    public void preDeregister() throws Exception {
+        super.preDeregister();
+    }
+
+    /**
+     * Allows the MBean to perform any operations needed after having been
+     * unregistered in the MBean server.
+     */
+    @Override
+    public void postDeregister() {
+        super.postDeregister();
+    }
+
+    public Set<ObjectName> listMatching(ObjectName pattern) {
+        return mbeanServer.queryNames(pattern, null);
+    }
+
+    private MBeanServer mbeanServer;
+
+    private ObjectName objectName;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/WombatMBean.java	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,59 @@
+
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+import java.util.Set;
+import javax.management.ObjectName;
+
+/**
+ * Interface WombatMBean
+ * Wombat Description
+ * @author dfuchs
+ */
+public interface WombatMBean
+{
+    /**
+     * This Wombat's mood on a [-100,+100] scale.
+     * -100 means that this wombat is very angry.
+     * @return The wombat's mood.
+     */
+    public int getMood();
+
+    /**
+     * Get A simple caption to describe a wombat
+     */
+    public String getCaption();
+
+    /**
+     * Set A simple caption to describe a wombat
+     */
+    public void setCaption(String value);
+
+    /**
+     * List matching MBeans in the same server.
+     * @param pattern an ObjectName pattern or null.
+     * @return A list of matching MBeans.
+     */
+    public Set<ObjectName> listMatching(ObjectName pattern);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/management/namespace/namespace.policy	Thu Sep 04 14:46:36 2008 +0200
@@ -0,0 +1,85 @@
+grant codebase "file:/-" {
+    permission java.util.PropertyPermission "jmx.wait", "read";
+    permission java.util.PropertyPermission "jmx.rmi.port", "read";
+    permission java.net.SocketPermission "*", "accept,connect,resolve";
+    permission java.security.SecurityPermission "*";
+
+    // Attribute Caption: allow get everywhere
+    // ==================
+
+    // allow getAttribute(*:*,Caption) in all MBeanServers
+    permission javax.management.MBeanPermission "#Caption", "getAttribute";
+    // allow getAttribute(*:*,Caption) in all namespaces recursively.
+    permission javax.management.namespace.JMXNamespacePermission "Caption",
+               "getAttribute";
+
+    // Attribute Mood: allow get only in MBeanServers named rmi*
+    // ===============
+
+    // allow to get attribute Mood of Wombat MBeans only in namespaces
+    // whose name match rmi*, wherever they are.
+    // for this we need two permissions:
+    permission javax.management.namespace.JMXNamespacePermission
+               "*::Mood[**//rmi*//wombat:*]",
+               "getAttribute";
+    permission javax.management.namespace.JMXNamespacePermission
+               "*::Mood[rmi*//wombat:*]",
+               "getAttribute";
+
+    // allow to get attribute mood in any MBeanServer whose name starts with
+    // rmi
+    permission javax.management.MBeanPermission "rmi*::#Mood",
+               "getAttribute";
+
+    // Attribute UUID:
+    // ===============
+
+    // allow to get attribute "UUID" everywhere.
+    permission javax.management.namespace.JMXNamespacePermission
+               "*::UUID[*//**//:*]",
+               "getAttribute";
+    permission javax.management.MBeanPermission
+               "#UUID[*//:*]",
+               "getAttribute";
+
+
+
+    // Let getMBeanInfo and queryNames through everywhere...
+    //
+    permission javax.management.namespace.JMXNamespacePermission "[]",
+               "getMBeanInfo,queryNames";
+    permission javax.management.MBeanPermission "*",
+               "getMBeanInfo,queryNames";
+
+    // special permission for all wombats:
+    //
+    permission javax.management.namespace.JMXNamespacePermission
+               "[**//*:type=Wombat,*]",
+               "getObjectInstance,isInstanceOf,queryMBeans";
+    permission javax.management.MBeanPermission "[*:type=Wombat,*]",
+               "getObjectInstance,isInstanceOf,queryMBeans";
+
+    // allow JMXNamespace::getDefaultDomain
+    permission javax.management.namespace.JMXNamespacePermission
+               "*::DefaultDomain",
+               "getAttribute";
+
+    // These permissions are required to connect visualvm.
+    //
+    permission javax.management.MBeanPermission "default::[java.lang:*]",
+               "getObjectInstance,isInstanceOf,getAttribute,getMBeanInfo,queryNames,queryMBeans";
+    permission javax.management.MBeanPermission "root::",
+               "isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance,getDomains";
+    permission javax.management.namespace.JMXNamespacePermission
+               "[**//JMImplementation:type=MBeanServerDelegate]",
+               "addNotificationListener,removeNotificationListener,isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance";
+    permission javax.management.MBeanPermission
+               "javax.management.MBeanServerDelegate",
+               "addNotificationListener,removeNotificationListener,isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance";
+
+    // Thread monitoring
+    permission java.lang.management.ManagementPermission "monitor";
+    permission javax.management.MBeanPermission "*::sun.management.*#*[java.lang:*]", "invoke";
+};
+
+