5072476: RFE: support cascaded (federated) MBean Servers
authordfuchs
Thu Sep 04 14:46:36 2008 +0200 (14 months ago)
changeset 5729145ff046bb4
parent 57100ea8fc81867
child 57371a5f3f55b9c
5072476: RFE: support cascaded (federated) MBean Servers
6299231: Add support for named MBean Servers
Summary: New javax.management.namespace package.
Reviewed-by: emcmanus
make/docs/CORE_PKGS.gmk
src/share/classes/com/sun/jmx/defaults/JmxProperties.java
src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java
src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java
src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java
src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java
src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java
src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java
src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java
src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java
src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java
src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java
src/share/classes/com/sun/jmx/mbeanserver/Repository.java
src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java
src/share/classes/com/sun/jmx/mbeanserver/Util.java
src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java
src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java
src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java
src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java
src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java
src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java
src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java
src/share/classes/com/sun/jmx/namespace/RoutingProxy.java
src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java
src/share/classes/com/sun/jmx/namespace/package.html
src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java
src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java
src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java
src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java
src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java
src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java
src/share/classes/com/sun/jmx/namespace/serial/package.html
src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java
src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java
src/share/classes/javax/management/InstanceNotFoundException.java
src/share/classes/javax/management/MBeanPermission.java
src/share/classes/javax/management/MBeanServer.java
src/share/classes/javax/management/MBeanServerDelegate.java
src/share/classes/javax/management/MBeanServerFactory.java
src/share/classes/javax/management/ObjectName.java
src/share/classes/javax/management/event/EventClient.java
src/share/classes/javax/management/event/EventClientDelegate.java
src/share/classes/javax/management/namespace/JMXDomain.java
src/share/classes/javax/management/namespace/JMXNamespace.java
src/share/classes/javax/management/namespace/JMXNamespaceMBean.java
src/share/classes/javax/management/namespace/JMXNamespacePermission.java
src/share/classes/javax/management/namespace/JMXNamespaceView.java
src/share/classes/javax/management/namespace/JMXNamespaces.java
src/share/classes/javax/management/namespace/JMXRemoteNamespace.java
src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java
src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java
src/share/classes/javax/management/namespace/MBeanServerSupport.java
src/share/classes/javax/management/namespace/VirtualEventManager.java
src/share/classes/javax/management/namespace/package-info.java
src/share/classes/javax/management/remote/JMXConnectorFactory.java
src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java
test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java
test/javax/management/ObjectName/ApplyWildcardTest.java
test/javax/management/namespace/DomainCreationTest.java
test/javax/management/namespace/EventWithNamespaceControlTest.java
test/javax/management/namespace/EventWithNamespaceTest.java
test/javax/management/namespace/ExportNamespaceTest.java
test/javax/management/namespace/JMXDomainTest.java
test/javax/management/namespace/JMXNamespaceSecurityTest.java
test/javax/management/namespace/JMXNamespaceTest.java
test/javax/management/namespace/JMXNamespaceViewTest.java
test/javax/management/namespace/JMXNamespacesTest.java
test/javax/management/namespace/JMXRemoteNamespaceTest.java
test/javax/management/namespace/JMXRemoteTargetNamespace.java
test/javax/management/namespace/LazyDomainTest.java
test/javax/management/namespace/MXBeanRefTest.java
test/javax/management/namespace/NamespaceController.java
test/javax/management/namespace/NamespaceControllerMBean.java
test/javax/management/namespace/NamespaceCreationTest.java
test/javax/management/namespace/NamespaceNotificationsTest.java
test/javax/management/namespace/NullDomainObjectNameTest.java
test/javax/management/namespace/NullObjectNameTest.java
test/javax/management/namespace/QueryNamesTest.java
test/javax/management/namespace/RemoveNotificationListenerTest.java
test/javax/management/namespace/RoutingServerProxyTest.java
test/javax/management/namespace/SerialParamProcessorTest.java
test/javax/management/namespace/SourceNamespaceTest.java
test/javax/management/namespace/VirtualMBeanNotifTest.java
test/javax/management/namespace/VirtualMBeanTest.java
test/javax/management/namespace/VirtualNamespaceQueryTest.java
test/javax/management/namespace/VirtualPropsTest.java
test/javax/management/namespace/Wombat.java
test/javax/management/namespace/WombatMBean.java
test/javax/management/namespace/namespace.policy
--- a/make/docs/CORE_PKGS.gmk Wed Sep 03 14:31:17 2008 +0200
+++ b/make/docs/CORE_PKGS.gmk Thu Sep 04 14:46:36 2008 +0200
@@ -158,6 +158,7 @@ CORE_PKGS =
javax.management.event \
javax.management.loading \
javax.management.monitor \
+ javax.management.namespace \
javax.management.relation \
javax.management.openmbean \
javax.management.timer \
--- a/src/share/classes/com/sun/jmx/defaults/JmxProperties.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/defaults/JmxProperties.java Thu Sep 04 14:46:36 2008 +0200
@@ -177,6 +177,18 @@ public class JmxProperties {
"javax.management.relation";
/**
+ * Logger name for Namespaces.
+ */
+ public static final String NAMESPACE_LOGGER_NAME =
+ "javax.management.namespace";
+
+ /**
+ * Logger name for Namespaces.
+ */
+ public static final Logger NAMESPACE_LOGGER =
+ Logger.getLogger(NAMESPACE_LOGGER_NAME);
+
+ /**
* Logger for Relation Service.
*/
public static final Logger RELATION_LOGGER =
--- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,33 +25,49 @@
package com.sun.jmx.interceptor;
-// java import
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.WeakHashMap;
+
+// JMX RI
+import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
+import com.sun.jmx.mbeanserver.DynamicMBean2;
+import com.sun.jmx.mbeanserver.Introspector;
+import com.sun.jmx.mbeanserver.MBeanInjector;
+import com.sun.jmx.mbeanserver.MBeanInstantiator;
+import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
+import com.sun.jmx.mbeanserver.NamedObject;
+import com.sun.jmx.mbeanserver.NotifySupport;
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
+import com.sun.jmx.mbeanserver.Util;
+import com.sun.jmx.remote.util.EnvHelp;
+
import java.lang.ref.WeakReference;
import java.security.AccessControlContext;
+import java.security.AccessController;
import java.security.Permission;
+import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
// JMX import
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
+import javax.management.DynamicWrapperMBean;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.JMRuntimeException;
import javax.management.ListenerNotFoundException;
-import javax.management.MalformedObjectNameException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanPermission;
@@ -64,6 +80,7 @@ import javax.management.NotCompliantMBea
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.RuntimeErrorExce
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 @@ import javax.management.NotificationBroa
*
* @since 1.5
*/
-public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
+public class DefaultMBeanServerInterceptor
+ extends MBeanServerInterceptorSupport {
/** The MBeanInstantiator object used by the
* DefaultMBeanServerInterceptor */
@@ -123,7 +126,7 @@ public class DefaultMBeanServerIntercept
* 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 @@ public class DefaultMBeanServerIntercept
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 sequence number identifyng the notifications sent */
+ /** The mbeanServerName */
+ private final String mbeanServerName;
+
+ /** The sequence number identifying the notifications sent */
// Now sequence number is handled by MBeanServerDelegate.
// private int sequenceNumber=0;
@@ -162,11 +167,13 @@ public class DefaultMBeanServerIntercept
* @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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
//
Set<NamedObject> list = repository.query(name, query);
- if (queryByRepo) {
- // The repository performs the filtering
- query = null;
- }
-
return (objectInstancesFromFilteredNamedObjects(list, query));
}
@@ -530,7 +536,7 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
//
Set<NamedObject> list = repository.query(name, query);
- if (queryByRepo) {
- // The repository performs the filtering
- query = null;
- }
-
return (objectNamesFromFilteredNamedObjects(list, query));
}
@@ -589,8 +590,8 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
}
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 @@ public class DefaultMBeanServerIntercept
// 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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
}
DynamicMBean instance = getMBean(name);
- checkMBeanPermission(instance, attribute.getName(),
+ checkMBeanPermission(mbeanServerName, instance, attribute.getName(),
name, "setAttribute");
try {
@@ -799,7 +803,7 @@ public class DefaultMBeanServerIntercept
// 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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
"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 @@ public class DefaultMBeanServerIntercept
if (logicalName != name && logicalName != null) {
logicalName =
- ObjectName.getInstance(nonDefaultDomain(logicalName));
- }
-
- checkMBeanPermission(classname, null, logicalName, "registerMBean");
+ ObjectName.getInstance(nonDefaultDomain(logicalName));
+ }
+
+ 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 @@ public class DefaultMBeanServerIntercept
//
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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
}
DynamicMBean instance = getMBean(name);
- checkMBeanPermission(instance, null, name, "addNotificationListener");
+ checkMBeanPermission(mbeanServerName, instance, null,
+ name, "addNotificationListener");
NotificationBroadcaster broadcaster =
getNotificationBroadcaster(name, instance,
@@ -1367,7 +1374,7 @@ public class DefaultMBeanServerIntercept
}
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 @@ public class DefaultMBeanServerIntercept
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 class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
throws InstanceNotFoundException {
DynamicMBean instance = getMBean(mbeanName);
- checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
+ checkMBeanPermission(mbeanServerName, instance, null, mbeanName,
+ "getClassLoaderFor");
return getResourceLoader(instance);
}
@@ -1513,12 +1522,13 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
}
} 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 @@ public class DefaultMBeanServerIntercept
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 @@ public class DefaultMBeanServerIntercept
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 =
@@ -1965,6 +1985,57 @@ public class DefaultMBeanServerIntercept
sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
logicalName);
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);
}
/**
@@ -2020,6 +2091,52 @@ public class DefaultMBeanServerIntercept
}
}
+
+ /**
+ * 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 @@ public class DefaultMBeanServerIntercept
*/
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;
}
+
+
}
--- a/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java Thu Sep 04 14:46:36 2008 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,35 +25,14 @@
package com.sun.jmx.interceptor;
-import java.util.Set;
-// RI import
-import javax.management.DynamicMBean;
-import javax.management.AttributeNotFoundException;
+import java.io.ObjectInputStream;
+import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
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.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.loading.ClassLoaderRepository;
/**
@@ -85,618 +64,67 @@ import javax.management.loading.ClassLoa
*
* @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;
+ /**
+ * This method should never be called.
+ * Usually throws UnsupportedOperationException.
+ */
+ public Object instantiate(String className, ObjectName loaderName)
+ throws ReflectionException, MBeanException,
+ InstanceNotFoundException;
+ /**
+ * This method should never be called.
+ * Usually throws UnsupportedOperationException.
+ */
+ public Object instantiate(String className, Object[] params,
+ String[] signature) 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,
+ Object[] params, String[] signature)
+ 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;
+ @Deprecated
+ public ObjectInputStream deserialize(ObjectName name, byte[] data)
+ throws InstanceNotFoundException, OperationsException;
/**
- * 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.
- *
+ * This method should never be called.
+ * Usually throws UnsupportedOperationException.
*/
- public void unregisterMBean(ObjectName name)
- throws InstanceNotFoundException, MBeanRegistrationException;
+ @Deprecated
+ public ObjectInputStream deserialize(String className, byte[] data)
+ throws OperationsException, ReflectionException;
/**
- * 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.
+ * This method should never be called.
+ * Usually hrows UnsupportedOperationException.
*/
- public ObjectInstance getObjectInstance(ObjectName name)
- throws InstanceNotFoundException;
+ @Deprecated
+ public ObjectInputStream deserialize(String className,
+ ObjectName loaderName, byte[] data)
+ throws InstanceNotFoundException, OperationsException,
+ ReflectionException;
/**
- * 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;
-
- /**
- * 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.
- */
- 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;
-
- /**
- * 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.
- */
- 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;
-
- /**
- * 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.
- */
- 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;
-
- /**
- * <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,
- 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();
}
+
--- a/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java Thu Sep 04 14:46:36 2008 +0200
@@ -51,6 +51,8 @@ import javax.management.ObjectName;
import javax.management.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 @@ public class SingleMBeanForwarder extend
if (!pattern.apply(mbeanName))
return false;
-// final String dompat = pattern.getDomain();
-// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
-// return true; // We already checked that patterns apply.
-//
-// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
-// // only matches if pattern ends with //
-// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
-// }
+ final String dompat = pattern.getDomain();
+ if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
+ return true; // We already checked that patterns apply.
+
+ if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
+ // only matches if pattern ends with //
+ return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
+ }
// should not come here, unless mbeanName contains a // in the
// middle of its domain, which would be weird.
--- a/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java Thu Sep 04 14:46:36 2008 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,43 +25,41 @@
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;
-
-// 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 java.util.Iterator;
+import java.util.Set;
+import java.util.logging.Level;
+
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
@@ -102,15 +100,14 @@ public final class JmxMBeanServer
/** true if interceptors are enabled **/
private final boolean interceptorsEnabled;
- /** Revisit: transient ??? **/
- private final transient MBeanServer outerShell;
-
- /** Revisit: transient ??? **/
- private transient MBeanServerInterceptor mbsInterceptor = null;
-
- /** Revisit: transient ??? **/
+ private final MBeanServer outerShell;
+
+ private volatile MBeanServer mbsInterceptor = null;
+
/** The MBeanServerDelegate object representing the MBean Server */
- private final transient MBeanServerDelegate mBeanServerDelegateObject;
+ private final MBeanServerDelegate mBeanServerDelegateObject;
+
+ private final String mbeanServerName;
/**
* <b>Package:</b> Creates an MBeanServer with the
@@ -243,9 +240,10 @@ public final class JmxMBeanServer
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 @@ public final class JmxMBeanServer
throws ReflectionException, MBeanException {
/* Permission check */
- checkMBeanPermission(className, null, null, "instantiate");
+ checkMBeanPermission(mbeanServerName, className, null, null,
+ "instantiate");
return instantiator.instantiate(className);
}
@@ -978,7 +977,8 @@ public final class JmxMBeanServer
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 @@ public final class JmxMBeanServer
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 @@ public final class JmxMBeanServer
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 @@ public final class JmxMBeanServer
"Unexpected exception occurred", e);
}
throw new
- IllegalStateException("Can't register delegate.");
+ IllegalStateException("Can't register delegate.",e);
}
@@ -1278,7 +1280,7 @@ public final class JmxMBeanServer
* 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 @@ public final class JmxMBeanServer
* @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 final class JmxMBeanServer
**/
public ClassLoaderRepository getClassLoaderRepository() {
/* Permission check */
- checkMBeanPermission(null, null, null, "getClassLoaderRepository");
+ checkMBeanPermission(mbeanServerName, null, null,
+ null, "getClassLoaderRepository");
return secureClr;
}
@@ -1484,14 +1487,16 @@ public final class JmxMBeanServer
// SECURITY CHECKS
//----------------
- private static void checkMBeanPermission(String classname,
+ private static void checkMBeanPermission(String serverName,
+ String classname,
String member,
ObjectName objectName,
String actions)
throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
- Permission perm = new MBeanPermission(classname,
+ Permission perm = new MBeanPermission(serverName,
+ classname,
member,
objectName,
actions);
--- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java Thu Sep 04 14:46:36 2008 +0200
@@ -224,7 +224,7 @@ public abstract class MXBeanLookup {
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 @@ public abstract class MXBeanLookup {
"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 @@ public abstract class MXBeanLookup {
currentLookup.set(lookup);
}
- // Method temporarily added until we have ObjectName.switchDomain in the
- // public API. Note that this method DOES NOT PRESERVE the order of
- // keys in the ObjectName so it must not be used in the final release.
- static ObjectName switchDomain(String domain, ObjectName name)
- throws MalformedObjectNameException {
- return new ObjectName(domain, name.getKeyPropertyList());
- }
-
private static final ThreadLocal<MXBeanLookup> currentLookup =
new ThreadLocal<MXBeanLookup>();
--- a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java Thu Sep 04 14:46:36 2008 +0200
@@ -45,7 +45,6 @@ import javax.management.RuntimeOperation
import javax.management.RuntimeOperationsException;
/**
- * The RepositorySupport implements the Repository interface.
* This repository does not support persistency.
*
* @since 1.5
@@ -197,9 +196,9 @@ public class Repository {
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 @@ public class Repository {
}
}
- /** 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 @@ public class Repository {
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 @@ public class Repository {
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 @@ public class Repository {
}
// 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 @@ public class Repository {
// need to reinstantiate a hashtable because of possible
// big buckets array size inside table, never cleared,
// thus the new !
- if (dom == domain)
+ if (dom == domain) // ES: OK dom and domain are interned.
domainTb.put(domain, new HashMap<String,NamedObject>());
}
--- a/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java Thu Sep 04 14:46:36 2008 +0200
@@ -28,17 +28,16 @@ import javax.management.MBeanServer;
import javax.management.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 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
- public MBeanServerInterceptor getMBeanServerInterceptor();
+ public MBeanServer getMBeanServerInterceptor();
/**
* Set the MBeanServerInterceptor.
@@ -77,7 +76,7 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
- public void setMBeanServerInterceptor(MBeanServerInterceptor interceptor);
+ public void setMBeanServerInterceptor(MBeanServer interceptor);
/**
* <p>Return the MBeanServerDelegate representing the MBeanServer.
--- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,8 @@
package com.sun.jmx.mbeanserver;
+import com.sun.jmx.defaults.JmxProperties;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,11 +44,22 @@ import java.util.TreeMap;
import java.util.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 @@ public class Util {
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 @@ public class Util {
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.
**/
@@ -214,6 +519,160 @@ public class Util {
public static ClassLoaderRepository getSingleClassLoaderRepository(
final ClassLoader loader) {
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) {
@@ -232,10 +691,19 @@ public class Util {
@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);
+ }
}
--- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Thu Sep 04 14:46:36 2008 +0200
@@ -57,6 +57,7 @@ import javax.security.auth.Subject;
public class ServerNotifForwarder {
+
public ServerNotifForwarder(MBeanServer mbeanServer,
Map env,
NotificationBuffer notifBuffer,
@@ -85,7 +86,8 @@ public class ServerNotifForwarder {
// 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 @@ public class ServerNotifForwarder {
// 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 @@ public class ServerNotifForwarder {
* 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 @@ public class ServerNotifForwarder {
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 @@ public class ServerNotifForwarder {
TargetedNotification tn) {
try {
if (checkNotificationEmission) {
- checkMBeanPermission(
- name, "addNotificationListener");
+ checkMBeanPermission(getMBeanServerName(),
+ mbeanServer, name, "addNotificationListener");
}
if (notificationAccessController != null) {
notificationAccessController.fetchNotification(
@@ -433,11 +432,27 @@ public class ServerNotifForwarder {
}
}
+ private String getMBeanServerName() {
+ if (mbeanServerName != null) return mbeanServerName;
+ else return (mbeanServerName = getMBeanServerName(mbeanServer));
+ }
+
+ private static String getMBeanServerName(final MBeanServer server) {
+ final PrivilegedAction<String> action = new PrivilegedAction<String>() {
+ public String run() {
+ return Util.getMBeanServerSecurityName(server);
+ }
+ };
+ return AccessController.doPrivileged(action);
+ }
+
+
//------------------
// PRIVATE VARIABLES
//------------------
private MBeanServer mbeanServer;
+ private volatile String mbeanServerName;
private final String connectionId;
--- a/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,7 @@
package com.sun.jmx.remote.util;
+import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.event.EventClientFactory;
import java.lang.reflect.InvocationHandler;
@@ -45,6 +46,7 @@ import javax.management.ObjectName;
import javax.management.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 @@ public class EventClientConnection imple
/**
* A logger for this class.
**/
- private static final Logger LOG =
- Logger.getLogger(EventClientConnection.class.getName());
-
- private static final String NAMESPACE_SEPARATOR = "//";
+ private static final Logger LOG = JmxProperties.NOTIFICATION_LOGGER;
+
private static final int NAMESPACE_SEPARATOR_LENGTH =
- NAMESPACE_SEPARATOR.length();
+ JMXNamespaces.NAMESPACE_SEPARATOR.length();
/**
* Creates a new {@code EventClientConnection}.
@@ -212,9 +212,9 @@ public class EventClientConnection imple
}
final ObjectName mbean = (ObjectName) args[0];
- final EventClient client = getEventClient();
-
- // Fails if client is null AND the MBean we try to listen to is
+ final EventClient evtClient = getEventClient();
+
+ // 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 @@ public class EventClientConnection imple
// 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 @@ public class EventClientConnection imple
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 @@ public class EventClientConnection imple
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 @@ public class EventClientConnection imple
case 4:
NotificationFilter filter = (NotificationFilter) args[2];
Object handback = args[3];
- if (client != null) {
- client.removeNotificationListener(mbean,
+ if (evtClient != null) {
+ evtClient.removeNotificationListener(mbean,
listener,
filter,
handback);
--- a/src/share/classes/javax/management/InstanceNotFoundException.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/InstanceNotFoundException.java Thu Sep 04 14:46:36 2008 +0200
@@ -51,4 +51,16 @@ public class InstanceNotFoundException e
public InstanceNotFoundException(String message) {
super(message);
}
+
+ /**
+ * Constructor for the frequent case where the message is the ObjectName
+ * of the missing MBean.
+ *
+ * @param name the ObjectName of the missing MBean.
+ *
+ * @since 1.7
+ */
+ public InstanceNotFoundException(ObjectName name) {
+ this(name.toString());
+ }
}
--- a/src/share/classes/javax/management/MBeanPermission.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanPermission.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,6 +25,7 @@
package javax.management;
+import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.Permission;
@@ -42,10 +43,10 @@ import java.security.Permission;
* 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>
*
@@ -56,6 +57,23 @@ import java.security.Permission;
* 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}
+ * 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>
*
@@ -88,7 +106,7 @@ import java.security.Permission;
* 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 @@ import java.security.Permission;
* </ul>
*
* <p>If you have an MBeanPermission, it allows operations only if all
- * four 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.
+ * five of the items match.</p>
+ *
+ * <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 @@ import java.security.Permission;
* <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 @@ public class MBeanPermission extends Per
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 @@ public class MBeanPermission extends Per
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 @@ public class MBeanPermission extends Per
// 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 @@ public class MBeanPermission extends Per
} 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 @@ public class MBeanPermission extends Per
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 @@ public class MBeanPermission extends Per
* 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 @@ public class MBeanPermission extends Per
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
@@ -417,6 +467,12 @@ public class MBeanPermission extends Per
* "<code>className#member[objectName]</code>" where each part is
* 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
@@ -439,17 +495,67 @@ public class MBeanPermission extends Per
String member,
ObjectName objectName,
String actions) {
-
- super(makeName(className, member, objectName));
- initName(className, member, objectName);
+ this("*",className,member,objectName,actions);
+ }
+
+ /**
+ * <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 @@ public class MBeanPermission extends Per
*
* <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>
*
@@ -1003,6 +1112,13 @@ public class MBeanPermission extends Per
* <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 className is "<code>*</code>", <i>p</i>'s
* className always matches it. If it is "<code>a.*</code>", <i>p</i>'s
@@ -1049,6 +1165,12 @@ public class MBeanPermission extends Per
}
// 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
@@ -1076,6 +1198,17 @@ public class MBeanPermission extends Per
expect that "that" contains a wildcard, since it is a
needed permission. So we assume that.classNameExactMatch. */
+ if (that.mbeanServerName == null) {
+ // bottom is implied
+ } else if (this.mbeanServerName == null) {
+ // bottom implies nothing but itself
+ return false;
+ } else if (that.mbeanServerName.equals(this.mbeanServerName)) {
+ // exact match
+ } else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) {
+ return false; // no match
+ }
+
if (that.classNamePrefix == null) {
// bottom is implied
} else if (this.classNamePrefix == null) {
--- a/src/share/classes/javax/management/MBeanServer.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServer.java Thu Sep 04 14:46:36 2008 +0200
@@ -42,7 +42,7 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
* <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 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
*
* <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 @@ import javax.management.loading.ClassLoa
*
* <li><p>For the {@link #unregisterMBean unregisterMBean} method,
* the caller's permissions must imply {@link
- * MBeanPermission#MBeanPermission(String,String,ObjectName,String)
- * MBeanPermission(className, null, name, "unregisterMBean")}.</p>
+ * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
+ * MBeanPermission(mbeanServerName, className, null, name, "unregisterMBean")}.
+ * </p>
*
* </ul>
*
--- a/src/share/classes/javax/management/MBeanServerDelegate.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServerDelegate.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,7 +25,9 @@
package javax.management;
+import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.defaults.ServiceName;
+import com.sun.jmx.mbeanserver.Util;
/**
* Represents the MBean server from the management point of view.
@@ -39,6 +41,7 @@ public class MBeanServerDelegate impleme
/** The MBean server agent identification.*/
private String mbeanServerId ;
+ private String mbeanServerName;
/** The NotificationBroadcasterSupport object that sends the
notifications */
@@ -68,6 +71,7 @@ public class MBeanServerDelegate impleme
public MBeanServerDelegate () {
stamp = getStamp();
broadcaster = new NotificationBroadcasterSupport() ;
+ mbeanServerName=null;
}
@@ -82,11 +86,100 @@ public class MBeanServerDelegate impleme
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.
}
/**
@@ -210,15 +303,8 @@ public class MBeanServerDelegate impleme
*
* @since 1.6
*/
- public static final ObjectName DELEGATE_NAME;
- static {
- try {
- DELEGATE_NAME =
- new ObjectName("JMImplementation:type=MBeanServerDelegate");
- } catch (MalformedObjectNameException e) {
- throw new Error("Can't initialize delegate name", e);
- }
- }
+ public static final ObjectName DELEGATE_NAME =
+ Util.newObjectName("JMImplementation:type=MBeanServerDelegate");
/* Return a timestamp that is monotonically increasing even if
System.currentTimeMillis() isn't (for example, if you call this
--- a/src/share/classes/javax/management/MBeanServerFactory.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/MBeanServerFactory.java Thu Sep 04 14:46:36 2008 +0200
@@ -25,15 +25,18 @@
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
@@ -80,9 +83,52 @@ import javax.management.loading.ClassLoa
* 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
@@ -222,37 +268,52 @@ public class MBeanServerFactory {
* <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");
-
- final MBeanServer mBeanServer = newMBeanServer(domain);
- addMBeanServer(mBeanServer);
- return mBeanServer;
- }
-
- /**
- * <p>Return a new object implementing the MBeanServer interface
- * with a standard default domain name, without keeping an
- * internal reference to this new object. The default 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 standard default domain name is
- * <code>DefaultDomain</code>.</p>
- *
- * <p>No reference is kept. <CODE>findMBeanServer</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>
- *
- * <p>This method is equivalent to <code>newMBeanServer(null)</code>.</p>
+ return createMBeanServer(null,domain);
+ }
+
+ /**
+ * <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}("newMBeanServer")</code>.
+ * @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
@@ -266,26 +327,37 @@ public class MBeanServerFactory {
* <code>javax.management.builder.initial</code> exists and can be
* instantiated but is not assignment compatible with {@link
* MBeanServerBuilder}.
- */
- public static MBeanServer newMBeanServer() {
- return newMBeanServer(null);
+ *
+ * @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);
}
/**
* <p>Return a new object implementing the MBeanServer interface
- * with the specified default domain name, without keeping an
- * internal reference to this new object. The given domain name
+ * with a standard default domain name, without keeping an
+ * internal reference to this new object. The default 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 standard default domain name is
+ * <code>DefaultDomain</code>.</p>
*
* <p>No reference is kept. <CODE>findMBeanServer</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 domain the default domain name for the created
- * MBeanServer. This is the value that will be returned by {@link
- * MBeanServer#getDefaultDomain}.
+ * <p>This method is equivalent to <code>newMBeanServer(null)</code>.</p>
*
* @return the newly created MBeanServer.
*
@@ -306,7 +378,128 @@ public class MBeanServerFactory {
* instantiated but is not assignment compatible with {@link
* MBeanServerBuilder}.
*/
+ public static MBeanServer newMBeanServer() {
+ return newMBeanServer(null);
+ }
+
+ /**
+ * <p>Return a new object implementing the MBeanServer interface
+ * with the specified default domain name, without keeping an
+ * internal reference to this new object. 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> 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 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}.
+ */
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 @@ public class MBeanServerFactory {
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 @@ public class MBeanServerFactory {
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 @@ public class MBeanServerFactory {
}
/**
+ * <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 class MBeanServerFactory {
*
**/
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 @@ public class MBeanServerFactory {
}
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 @@ public class MBeanServerFactory {
**/
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 @@ public class MBeanServerFactory {
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 @@ public class MBeanServerFactory {
builder = newBuilder(newBuilderClass);
} catch (ClassNotFoundException x) {
final String msg =
- "Failed to load MBeanServerBuilder class " +
- builderClassName + ": " + x;
+ "Failed to load MBeanServerBuilder class " +
+ builderClassName + ": " + x;
throw new JMRuntimeException(msg, x);
}
} catch (RuntimeException x) {
--- a/src/share/classes/javax/management/ObjectName.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/ObjectName.java Thu Sep 04 14:46:36 2008 +0200
@@ -27,6 +27,8 @@ package javax.management;
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 Compa
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 @@ public class ObjectName implements Compa
/**
* 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 @@ public class ObjectName implements Compa
*/
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 @@ public class ObjectName implements Compa
}
// 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 @@ public class ObjectName implements Compa
// 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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
// Serializes this instance in the new serial form
//
out.defaultWriteObject();
- out.writeObject(getSerializedNameString());
+
+ out.writeObject(changeContext(ctxt,getSerializedNameString()));
}
}
@@ -1394,6 +1458,27 @@ public class ObjectName implements Compa
if (name.getClass().equals(ObjectName.class))
return name;
return Util.newObjectName(name.getSerializedNameString());
+ }
+
+ /**
+ * 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);
}
/**
@@ -1550,7 +1635,7 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
}
}
- return new String(dest_chars);
+ final String name = new String(dest_chars);
+ return name;
}
/**
@@ -1734,7 +1822,7 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
// (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 @@ public class ObjectName implements Compa
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 @@ public class ObjectName implements Compa
// 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 @@ public class ObjectName implements Compa
//
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 @@ public class ObjectName implements Compa
* @since 1.6
*/
public int compareTo(ObjectName name) {
+ // Quick optimization:
+ //
+ if (name == this) return 0;
+
// (1) Compare domains
//
int domainValue = this.getDomain().compareTo(name.getDomain());
--- a/src/share/classes/javax/management/event/EventClient.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/event/EventClient.java Thu Sep 04 14:46:36 2008 +0200
@@ -29,6 +29,7 @@ import com.sun.jmx.event.LeaseRenewer;
import com.sun.jmx.event.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 @@ public class EventClient implements Even
return clientId;
}
+ /**
+ * Returns a JMX Connector that will use an {@link EventClient}
+ * to subscribe for notifications. If the server doesn't have
+ * an {@link EventClientDelegateMBean}, then the connector will
+ * use the legacy notification mechanism instead.
+ *
+ * @param wrapped The underlying JMX Connector wrapped by the returned
+ * connector.
+ *
+ * @return A JMX Connector that will uses an {@link EventClient}, if
+ * available.
+ *
+ * @see EventClient#getEventClientConnection(MBeanServerConnection)
+ */
+ public static JMXConnector withEventClient(final JMXConnector wrapped) {
+ return JMXNamespaceUtils.withEventClient(wrapped);
+ }
+
private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
leaseRenewerThreadPool = PerThreadGroupPool.make();
}
--- a/src/share/classes/javax/management/event/EventClientDelegate.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/event/EventClientDelegate.java Thu Sep 04 14:46:36 2008 +0200
@@ -721,7 +721,10 @@ public class EventClientDelegate impleme
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 @@ public class EventClientDelegate impleme
String classname = oi.getClassName();
MBeanPermission perm = new MBeanPermission(
+ serverName,
classname,
null,
name,
@@ -746,6 +750,20 @@ public class EventClientDelegate impleme
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
// ------------------------------------
--- a/src/share/classes/javax/management/remote/JMXConnectorFactory.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/remote/JMXConnectorFactory.java Thu Sep 04 14:46:36 2008 +0200
@@ -268,6 +268,14 @@ public class JMXConnectorFactory {
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 class JMXConnectorFactory {
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 @@ public class JMXConnectorFactory {
}
}
- envcopy = Collections.unmodifiableMap(envcopy);
-
- return provider.newJMXConnector(serviceURL, envcopy);
+ final Map<String,Object> fixedenv =
+ Collections.unmodifiableMap(envcopy);
+
+ return provider.newJMXConnector(serviceURL, fixedenv);
}
private static String resolvePkgs(Map env) throws JMXProviderException {
@@ -365,8 +376,8 @@ public class JMXConnectorFactory {
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 @@ public class JMXConnectorFactory {
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 @@ public class JMXConnectorFactory {
}
if (loader == null)
- loader =
- AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ loader = AccessController.doPrivileged(
+ new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return
Thread.currentThread().getContextClassLoader();
--- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Wed Sep 03 14:31:17 2008 +0200
+++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Thu Sep 04 14:46:36 2008 +0200
@@ -77,6 +77,7 @@ import javax.management.event.EventClien
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 class RMIConnectionImpl implement
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 class RMIConnectionImpl implement
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 @@ public class RMIConnectionImpl implement
private final boolean checkNotificationEmission;
private final String clientId;
private final String connectionId;
+ private volatile String mbeanServerName;
EventSubscriptionManager(
MBeanServer mbeanServer,
@@ -1343,6 +1369,11 @@ public class RMIConnectionImpl implement
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 @@ public class RMIConnectionImpl implement
try {
if (checkNotificationEmission) {
ServerNotifForwarder.checkMBeanPermission(
- mbeanServer, name, "addNotificationListener");
+ mbeanServerName(), mbeanServer, name,
+ "addNotificationListener");
}
notifAC.fetchNotification(
connectionId, name, notification, getSubject());
@@ -1392,7 +1424,7 @@ public class RMIConnectionImpl implement
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 @@ public class RMIConnectionImpl implement
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 @@ public class RMIConnectionImpl implement
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];
--- a/test/javax/management/ObjectName/ApplyWildcardTest.java Wed Sep 03 14:31:17 2008 +0200
+++ b/test/javax/management/ObjectName/ApplyWildcardTest.java Thu Sep 04 14:46:36 2008 +0200
@@ -28,10 +28,13 @@
* with wildcards in the key properties value part.
* @author Luis-Miguel Alventosa
* @run clean ApplyWildcardTest
+ * @compile -XDignore.symbol.file=true ApplyWildcardTest.java
* @run build ApplyWildcardTest
* @run main ApplyWildcardTest
*/
+import com.sun.jmx.mbeanserver.Repository;
+import com.sun.jmx.mbeanserver.Util;
import javax.management.ObjectName;
public class ApplyWildcardTest {
@@ -74,6 +77,75 @@ public class ApplyWildcardTest {
{ "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 @@ public class ApplyWildcardTest {
{ "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 @@ public class ApplyWildcardTest {
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,9 +269,84 @@ public class ApplyWildcardTest {
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;
+
+ int error = 0;
+
+ if (!(new ObjectName("z*x*:*").apply(new ObjectName("zaxcx//:k=v"))))
+ throw new Exception();
+
// Check null values
//
@@ -253,6 +429,10 @@ public class ApplyWildcardTest {
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/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 = getInterceptorOrNullFo