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