From 4d1ecb3603bea837943fe9ba277dc9406d0f155b Mon Sep 17 00:00:00 2001 From: dfuchs Date: Tue, 9 Dec 2008 20:20:48 +0100 Subject: [PATCH] 6768935: Clarify the behaviour of ObjectName pattern matching with regards to namespaces Reviewed-by: emcmanus --- .../jmx/interceptor/DispatchInterceptor.java | 16 +-- .../DomainDispatchInterceptor.java | 30 ++--- .../NamespaceDispatchInterceptor.java | 125 +++++++++++++----- .../com/sun/jmx/mbeanserver/MXBeanLookup.java | 11 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 1 - .../sun/jmx/namespace/DomainInterceptor.java | 11 +- .../jmx/namespace/NamespaceInterceptor.java | 9 +- .../sun/jmx/namespace/ObjectNameRouter.java | 13 +- .../RoutingMBeanServerConnection.java | 31 ++--- .../com/sun/jmx/namespace/RoutingProxy.java | 6 +- .../classes/javax/management/MBeanServer.java | 10 +- .../management/MBeanServerConnection.java | 34 ++++- .../classes/javax/management/ObjectName.java | 106 ++++++++++++--- .../javax/management/namespace/JMXDomain.java | 10 +- .../namespace/JMXNamespacePermission.java | 38 +----- .../management/namespace/JMXNamespaces.java | 12 +- .../management/namespace/package-info.java | 36 ++++- .../namespace/LeadingSeparatorsTest.java | 23 +++- .../namespace/NullDomainObjectNameTest.java | 35 ++++- .../namespace/NullObjectNameTest.java | 29 +++- .../management/namespace/QueryNamesTest.java | 66 ++++++++- 21 files changed, 441 insertions(+), 211 deletions(-) diff --git a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java index 4a79567fe..f68e539d2 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java @@ -44,7 +44,6 @@ 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; @@ -205,8 +204,7 @@ public abstract class DispatchInterceptor // Returns the ObjectName of the JMXNamespace (or JMXDomain) for that // key (a namespace or a domain name). - abstract ObjectName getHandlerNameFor(String key) - throws MalformedObjectNameException; + abstract ObjectName getHandlerNameFor(String key); // Creates an interceptor for the given key, name, JMXNamespace (or // JMXDomain). Note: this will be either a NamespaceInterceptor @@ -263,14 +261,10 @@ public abstract class DispatchInterceptor void validateHandlerNameFor(String key, ObjectName name) { if (key == null || key.equals("")) throw new IllegalArgumentException("invalid key for "+name+": "+key); - try { - final ObjectName handlerName = getHandlerNameFor(key); - if (!name.equals(handlerName)) - throw new IllegalArgumentException("bad handler name: "+name+ - ". Should be: "+handlerName); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(name.toString(),x); - } + final ObjectName handlerName = getHandlerNameFor(key); + if (!name.equals(handlerName)) + throw new IllegalArgumentException("bad handler name: "+name+ + ". Should be: "+handlerName); } // Called by the DefaultMBeanServerInterceptor when an instance diff --git a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java index 9b9b1d6b1..388af6b21 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java @@ -38,7 +38,6 @@ import java.util.logging.Logger; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.namespace.JMXDomain; @@ -248,21 +247,17 @@ class DomainDispatchInterceptor if (pattern == null) return true; if (pattern.isDomainPattern()) return true; - try { - // case b) above. - // - // This is a bit of a hack. If there's any chance that a JMXDomain - // MBean name is selected by the given pattern then we must include - // the local namespace in our search. - // - // Returning true will have this effect. see 2. above. - // - if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain()))) - return true; - } catch (MalformedObjectNameException x) { - // should not happen - throw new IllegalArgumentException(String.valueOf(pattern), x); - } + // case b) above. + // + // This is a bit of a hack. If there's any chance that a JMXDomain + // MBean name is selected by the given pattern then we must include + // the local namespace in our search. + // + // Returning true will have this effect. see 2. above. + // + if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain()))) + return true; + return false; } @@ -291,8 +286,7 @@ class DomainDispatchInterceptor } @Override - final ObjectName getHandlerNameFor(String key) - throws MalformedObjectNameException { + final ObjectName getHandlerNameFor(String key) { return JMXDomain.getDomainObjectName(key); } diff --git a/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java index d86c787b8..5ab61f893 100644 --- a/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java @@ -37,8 +37,8 @@ import java.util.logging.Logger; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; import javax.management.namespace.JMXDomain; import javax.management.namespace.JMXNamespace; import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; @@ -60,6 +60,7 @@ public class NamespaceDispatchInterceptor private static final int NAMESPACE_SEPARATOR_LENGTH = NAMESPACE_SEPARATOR.length(); + private static final ObjectName X3 = ObjectName.valueOf("x:x=x"); private final DomainDispatchInterceptor nextInterceptor; private final String serverName; @@ -89,27 +90,38 @@ public class NamespaceDispatchInterceptor serverName = Util.getMBeanServerSecurityName(delegate); } - // TODO: Should move that to JMXNamespace? or to ObjectName? /** * Get first name space in ObjectName path. Ignore leading namespace - * separators. + * separators. Includes the trailing //. + * + * Examples: + *
+     *  For ObjectName:                   Returns:
+     *  foo//bar//baz:x=x         ->      "foo//"
+     *  foo//:type=JMXNamespace   ->      "foo//"
+     *  foo//:x=x                 ->      "foo//"
+     *  foo////:x=x               ->      "foo//"
+     *  //foo//bar//baz:x=x       ->      "//"
+     *  ////foo//bar//baz:x=x     ->      "//"
+     *  //:x=x                    ->      "//"
+     *  foo:x=x                   ->      ""
+     *  (null)                    ->      ""
+     *  :x=x                      ->      ""
+     *
+     * 
**/ - static String getFirstNamespace(ObjectName name) { + static String getFirstNamespaceWithSlash(ObjectName name) { if (name == null) return ""; final String domain = name.getDomain(); if (domain.equals("")) return ""; - // skip leading separators - int first = 0; - while (domain.startsWith(NAMESPACE_SEPARATOR,first)) - first += NAMESPACE_SEPARATOR_LENGTH; - // go to next separator - final int end = domain.indexOf(NAMESPACE_SEPARATOR,first); + final int end = domain.indexOf(NAMESPACE_SEPARATOR); if (end == -1) return ""; // no namespace // This is the first element in the namespace path. - final String namespace = domain.substring(first,end); + final String namespace = + domain.substring(0,end+NAMESPACE_SEPARATOR_LENGTH); return namespace; } @@ -130,27 +142,49 @@ public class NamespaceDispatchInterceptor resource.getClass().getName()); } - final boolean isLocalHandlerNameFor(String namespace, - ObjectName handlerName) { - return handlerName.getDomain().equals(namespace+NAMESPACE_SEPARATOR) && - JMXNamespace.TYPE_ASSIGNMENT.equals( - handlerName.getKeyPropertyListString()); + // Removes the trailing //. namespaceWithSlash should be either + // "" or a namespace path ending with //. + // + private final String getKeyFor(String namespaceWithSlash) { + final int end = namespaceWithSlash.length() - + NAMESPACE_SEPARATOR_LENGTH; + if (end <= 0) return ""; + final String key = namespaceWithSlash.substring(0,end); + return key; } @Override final MBeanServer getInterceptorOrNullFor(ObjectName name) { - final String namespace = getFirstNamespace(name); - if (namespace.equals("") || isLocalHandlerNameFor(namespace,name) || - name.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) { + final String namespace = getFirstNamespaceWithSlash(name); + + // Leading separators should trigger instance not found exception. + // returning null here has this effect. + // + if (namespace.equals(NAMESPACE_SEPARATOR)) { + LOG.finer("ObjectName starts with: "+namespace); + return null; + } + + // namespace="" means that there was no namespace path in the + // ObjectName. => delegate to the next interceptor (local MBS) + // name.getDomain()=namespace means that we have an ObjectName of + // the form blah//:x=x. This is either a JMXNamespace or a non + // existent MBean. => delegate to the next interceptor (local MBS) + if (namespace.equals("") || name.getDomain().equals(namespace)) { LOG.finer("dispatching to local name space"); return nextInterceptor; } - final NamespaceInterceptor ns = getInterceptor(namespace); + + // There was a namespace path in the ObjectName. Returns the + // interceptor that handles it, or null if there is no such + // interceptor. + final String key = getKeyFor(namespace); + final NamespaceInterceptor ns = getInterceptor(key); if (LOG.isLoggable(Level.FINER)) { if (ns != null) { - LOG.finer("dispatching to name space: " + namespace); + LOG.finer("dispatching to name space: " + key); } else { - LOG.finer("no handler for: " + namespace); + LOG.finer("no handler for: " + key); } } return ns; @@ -158,18 +192,44 @@ public class NamespaceDispatchInterceptor @Override final QueryInterceptor getInterceptorForQuery(ObjectName pattern) { - final String namespace = getFirstNamespace(pattern); - if (namespace.equals("") || isLocalHandlerNameFor(namespace,pattern) || - pattern.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) { + final String namespace = getFirstNamespaceWithSlash(pattern); + + // Leading separators should trigger instance not found exception. + // returning null here has this effect. + // + if (namespace.equals(NAMESPACE_SEPARATOR)) { + LOG.finer("ObjectName starts with: "+namespace); + return null; + } + + // namespace="" means that there was no namespace path in the + // ObjectName. => delegate to the next interceptor (local MBS) + // name.getDomain()=namespace means that we have an ObjectName of + // the form blah//:x=x. This is either a JMXNamespace or a non + // existent MBean. => delegate to the next interceptor (local MBS) + if (namespace.equals("") || pattern.getDomain().equals(namespace)) { LOG.finer("dispatching to local name space"); return new QueryInterceptor(nextInterceptor); } - final NamespaceInterceptor ns = getInterceptor(namespace); + + // This is a 'hack' to check whether the first namespace is a pattern. + // We wan to throw RTOE wrapping IAE in that case + if (X3.withDomain(namespace).isDomainPattern()) { + throw new RuntimeOperationsException( + new IllegalArgumentException("Pattern not allowed in namespace path")); + } + + // There was a namespace path in the ObjectName. Returns the + // interceptor that handles it, or null if there is no such + // interceptor. + // + final String key = getKeyFor(namespace); + final NamespaceInterceptor ns = getInterceptor(key); if (LOG.isLoggable(Level.FINER)) { if (ns != null) { - LOG.finer("dispatching to name space: " + namespace); + LOG.finer("dispatching to name space: " + key); } else { - LOG.finer("no handler for: " + namespace); + LOG.finer("no handler for: " + key); } } if (ns == null) return null; @@ -177,15 +237,16 @@ public class NamespaceDispatchInterceptor } @Override - final ObjectName getHandlerNameFor(String key) - throws MalformedObjectNameException { - return ObjectName.getInstance(key+NAMESPACE_SEPARATOR, + final ObjectName getHandlerNameFor(String key) { + return ObjectName.valueOf(key+NAMESPACE_SEPARATOR, "type", JMXNamespace.TYPE); } @Override final public String getHandlerKey(ObjectName name) { - return getFirstNamespace(name); + final String namespace = getFirstNamespaceWithSlash(name); + // namespace is either "" or a namespace ending with // + return getKeyFor(namespace); } @Override diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java index 227c1eec9..605c67f16 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java @@ -37,7 +37,6 @@ import javax.management.InstanceAlreadyExistsException; import javax.management.JMX; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.openmbean.OpenDataException; @@ -225,7 +224,7 @@ public abstract class MXBeanLookup { String domain = prefix + name.getDomain(); try { name = name.withDomain(domain); - } catch (MalformedObjectNameException e) { + } catch (IllegalArgumentException e) { throw EnvHelp.initCause( new InvalidObjectException(e.getMessage()), e); } @@ -239,12 +238,14 @@ public abstract class MXBeanLookup { String domain = name.getDomain(); if (!domain.startsWith(prefix)) { throw new OpenDataException( - "Proxy's name does not start with " + prefix + ": " + name); + "Proxy's name does not start with " + + prefix + ": " + name); } try { name = name.withDomain(domain.substring(prefix.length())); - } catch (MalformedObjectNameException e) { - throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e); + } catch (IllegalArgumentException e) { + throw EnvHelp.initCause( + new OpenDataException(e.getMessage()), e); } return name; } diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index 5fe4d1542..1e575e419 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -48,7 +48,6 @@ 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; diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java index 7b88087a8..6e56c8385 100644 --- a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -42,7 +42,6 @@ import javax.management.ListenerNotFoundException; import javax.management.MBeanPermission; import javax.management.MBeanServerDelegate; import javax.management.MBeanServerNotification; -import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationFilter; import javax.management.NotificationListener; @@ -268,13 +267,9 @@ public class DomainInterceptor extends HandlerInterceptor { // When we reach here, it has been verified that 'name' matches our domain // name (done by DomainDispatchInterceptor) private ObjectName getPatternFor(final ObjectName name) { - try { - if (name == null) return ALL; - if (name.getDomain().equals(domainName)) return name; - return name.withDomain(domainName); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(String.valueOf(name),x); - } + if (name == null) return ALL; + if (name.getDomain().equals(domainName)) return name; + return name.withDomain(domainName); } @Override diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java index 6862066d2..df03ed5ec 100644 --- a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -24,15 +24,12 @@ */ package com.sun.jmx.namespace; -import com.sun.jmx.defaults.JmxProperties; import java.util.ArrayList; import java.util.List; -import java.util.logging.Logger; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespacePermission; @@ -114,14 +111,12 @@ public class NamespaceInterceptor extends HandlerInterceptor { } @Override - protected ObjectName toSource(ObjectName targetName) - throws MalformedObjectNameException { + protected ObjectName toSource(ObjectName targetName) { return proc.toSourceContext(targetName, true); } @Override - protected ObjectName toTarget(ObjectName sourceName) - throws MalformedObjectNameException { + protected ObjectName toTarget(ObjectName sourceName) { return proc.toTargetContext(sourceName, false); } diff --git a/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java b/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java index 7d668169f..55a646540 100644 --- a/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java +++ b/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java @@ -27,7 +27,6 @@ package com.sun.jmx.namespace; import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; -import javax.management.MalformedObjectNameException; import javax.management.ObjectInstance; import javax.management.ObjectName; @@ -83,11 +82,7 @@ public class ObjectNameRouter { } final String targetDomain = (tlen>0?targetPrefix+NAMESPACE_SEPARATOR+srcDomain:srcDomain); - try { - return sourceName.withDomain(targetDomain); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(String.valueOf(sourceName),x); - } + return sourceName.withDomain(targetDomain); } public final ObjectName toSourceContext(ObjectName targetName, @@ -113,11 +108,7 @@ public class ObjectNameRouter { final String sourceDomain = (slen>0?sourcePrefix+NAMESPACE_SEPARATOR+targetDomain: targetDomain); - try { - return targetName.withDomain(sourceDomain); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(String.valueOf(targetName),x); - } + return targetName.withDomain(sourceDomain); } public final ObjectInstance toTargetContext(ObjectInstance sourceMoi, diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java index 7022e7e29..49b829004 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -46,7 +46,6 @@ import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanRegistrationException; import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.NotificationFilter; import javax.management.NotificationListener; @@ -100,18 +99,17 @@ public abstract class RoutingMBeanServerConnection public T source() { return source; } @Override - public ObjectName toSource(ObjectName targetName) - throws MalformedObjectNameException { + public ObjectName toSource(ObjectName targetName) { if (targetName == null) return null; if (targetName.getDomain().equals("") && targetNs.equals("")) { try { @@ -229,8 +228,7 @@ public abstract class RoutingProxy } @Override - public ObjectName toTarget(ObjectName sourceName) - throws MalformedObjectNameException { + public ObjectName toTarget(ObjectName sourceName) { if (sourceName == null) return null; return router.toTargetContext(sourceName,false); } diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index 6e4ee0a69..d602eadbc 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -424,10 +424,16 @@ public interface MBeanServer extends MBeanServerConnection { public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException; - // doc comment inherited from MBeanServerConnection + /** + * {@inheritDoc} + * @throws RuntimeOperationsException {@inheritDoc} + */ public Set queryMBeans(ObjectName name, QueryExp query); - // doc comment inherited from MBeanServerConnection + /** + * {@inheritDoc} + * @throws RuntimeOperationsException {@inheritDoc} + */ public Set queryNames(ObjectName name, QueryExp query); // doc comment inherited from MBeanServerConnection diff --git a/src/share/classes/javax/management/MBeanServerConnection.java b/src/share/classes/javax/management/MBeanServerConnection.java index add96cfcb..11509df53 100644 --- a/src/share/classes/javax/management/MBeanServerConnection.java +++ b/src/share/classes/javax/management/MBeanServerConnection.java @@ -436,7 +436,17 @@ public interface MBeanServerConnection extends NotificationManager { * 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. + * selecting MBeans. ObjectName patterns that may be contained in the + * query expression will be + * evaluated in the context of the + * {@link javax.management.namespace namespace} + * in which the MBeans selected by {@code name} are registered. + * Thus, in the {@code query} parameter, no ObjectName pattern containing a + * namespace path can match any of the MBean names selected by {@code name}. + * See the + * namespaces documentation for more details. * * @return A set containing the ObjectInstance * objects for the selected MBeans. If no MBean satisfies the @@ -444,6 +454,11 @@ public interface MBeanServerConnection extends NotificationManager { * * @exception IOException A communication problem occurred when * talking to the MBean server. + * @exception RuntimeOperationsException Wraps a + * java.lang.IllegalArgumentException: The name + * parameter contains an invalid pattern. See the + * namespaces documentation for more details. */ public Set queryMBeans(ObjectName name, QueryExp query) throws IOException; @@ -464,7 +479,17 @@ public interface MBeanServerConnection extends NotificationManager { * 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. + * selecting MBeans. ObjectName patterns that may be contained in the + * query expression will be + * evaluated in the context of the + * {@link javax.management.namespace namespace} + * in which the MBeans slected by {@code name} are registered. + * Thus, in the {@code query} parameter, no ObjectName pattern containing a + * namespace path can match any of the MBean names selected by {@code name}. + * See the + * namespaces documentation for more details. * * @return A set containing the ObjectNames for the MBeans * selected. If no MBean satisfies the query, an empty list is @@ -472,6 +497,11 @@ public interface MBeanServerConnection extends NotificationManager { * * @exception IOException A communication problem occurred when * talking to the MBean server. + * @exception RuntimeOperationsException Wraps a + * java.lang.IllegalArgumentException: The name + * parameter contains an invalid pattern. See the + * namespaces documentation for more details. */ public Set queryNames(ObjectName name, QueryExp query) throws IOException; diff --git a/src/share/classes/javax/management/ObjectName.java b/src/share/classes/javax/management/ObjectName.java index 8185edc27..41025a768 100644 --- a/src/share/classes/javax/management/ObjectName.java +++ b/src/share/classes/javax/management/ObjectName.java @@ -56,14 +56,38 @@ import java.util.Map; * properties.

* *

The domain is a string of characters not including - * the character colon (:). It is recommended that the domain - * should not contain the string "{@code //}", which is reserved for future use. + * the character colon (:).

+ *

Starting with the version 2.0 of the JMX specification, the + * domain can also start with a {@linkplain + * javax.management.namespace#NamespacePrefix namespace prefix} identifying + * the {@linkplain javax.management.namespace namespace} in which the + * MBean is registered. A namespace prefix is a path string where + * elements are separated by a double slash (//). + * It identifies the {@linkplain javax.management.namespace namespace} in + * which the MBean so named is registered.

+ * + *

For instance the ObjectName bar//baz:k=v identifiies an MBean + * named baz:k=v in the namespace bar. Similarly the + * ObjectName foo//bar//baz:k=v identifiies an MBean named + * baz:k=v in the namespace foo//bar. See the {@linkplain + * javax.management.namespace namespace} documentation for more details.

* *

If the domain includes at least one occurrence of the wildcard * characters asterisk (*) or question mark * (?), then the object name is a pattern. The asterisk * matches any sequence of zero or more characters, while the question - * mark matches any single character.

+ * mark matches any single character.
+ * A namespace separator // does not match wildcard + * characters unless it is at the very end of the domain string. + * So foo*bar*:* does not match foo//bar:k=v but it + * does match fooxbar//:k=v. + *

+ * + *

When included in a namespace path the special path element + * ** matches any number of sub namespaces + * recursively, but only if used as a complete namespace path element, + * as in **//b//c//D:k=v or a//**//c//D:k=v + * - see below. * *

If the domain is empty, it will be replaced in certain contexts * by the default domain of the MBean server in which the @@ -171,6 +195,51 @@ import java.util.Map; * with {@code \}. * * + *

Pattern and namespaces:

+ *

In an object name pattern, a path element + * of exactly ** corresponds to a meta + * wildcard that will match any number of sub namespaces.
Hence:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
patternmatchesdoesn't match
  • **//D:k=v
a//D:k=v
+ * a//b//D:k=v
+ * a//b//c//D:k=v
D:k=v
  • a//**//D:k=v
a//b//D:k=v
+ * a//b//c//D:k=v
b//b//c//D:k=v
+ * a//D:k=v
+ * D:k=v
  • a//**//e//D:k=v
a//b//e//D:k=v
+ * a//b//c//e//D:k=v
a//b//c//c//D:k=v
+ * b//b//c//e//D:k=v
+ * a//e//D:k=v
+ * e//D:k=v
  • a//b**//e//D:k=v
a//b//e//D:k=va//b//c//e//D:k=v
+ * because in that case b**
+ * is not a meta-wildcard - and b**
+ * is thus equivalent to b*.
+ * + *

+ *

+ * Note: Although ObjectName patterns where the characters + * * and ? appear in the namespace path are legal, + * they are not valid in the {@code name} parameter of the MBean Server's + * {@link MBeanServer#queryNames queryNames} and {@link MBeanServer#queryMBeans + * queryMBeans} methods. See the + * namespaces documentation for more details. + *

+ * *

An ObjectName can be written as a String with the following * elements in order:

* @@ -439,11 +508,6 @@ public class ObjectName implements Comparable, QueryExp { _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 <======================================= @@ -1096,11 +1160,10 @@ public class ObjectName implements Comparable, QueryExp { */ private boolean isDomain(String domain) { if (domain == null) return true; - final char[] d=domain.toCharArray(); - final int len = d.length; + final int len = domain.length(); int next = 0; while (next < len) { - final char c = d[next++]; + final char c = domain.charAt(next++); switch (c) { case ':' : case '\n' : @@ -1234,12 +1297,6 @@ public class ObjectName implements Comparable, QueryExp { 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; } @@ -1584,13 +1641,18 @@ public class ObjectName implements Comparable, QueryExp { * @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. + * @exception IllegalArgumentException The {@code newDomain} passed as a + * parameter does not have the right format. The {@linkplain + * Throwable#getCause() cause} of this exception will be a + * {@link MalformedObjectNameException}. * @since 1.7 **/ - public final ObjectName withDomain(String newDomain) - throws MalformedObjectNameException { - return new ObjectName(newDomain, this); + public final ObjectName withDomain(String newDomain) { + try { + return new ObjectName(newDomain, this); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(x.getMessage(),x); + } } /** diff --git a/src/share/classes/javax/management/namespace/JMXDomain.java b/src/share/classes/javax/management/namespace/JMXDomain.java index bff3c1370..ca108bf12 100644 --- a/src/share/classes/javax/management/namespace/JMXDomain.java +++ b/src/share/classes/javax/management/namespace/JMXDomain.java @@ -35,7 +35,6 @@ import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; /** @@ -291,12 +290,9 @@ public class JMXDomain extends JMXNamespace { public static ObjectName getDomainObjectName(String domain) { if (domain == null) return null; if (domain.contains(NAMESPACE_SEPARATOR)) - throw new IllegalArgumentException(domain); - try { - return ObjectName.getInstance(domain, "type", TYPE); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(domain,x); - } + throw new IllegalArgumentException("domain contains " + + NAMESPACE_SEPARATOR+": "+domain); + return ObjectName.valueOf(domain, "type", TYPE); } diff --git a/src/share/classes/javax/management/namespace/JMXNamespacePermission.java b/src/share/classes/javax/management/namespace/JMXNamespacePermission.java index 5dd012672..62608cb06 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespacePermission.java +++ b/src/share/classes/javax/management/namespace/JMXNamespacePermission.java @@ -136,7 +136,8 @@ import java.security.Permission; * ** matches any number of sub namespaces * recursively, but only if used as a complete namespace path element, * as in **//b//c//D:k=v or a//**//c//D:k=v - * - see below. + * - see ObjectName documentation + * for more details. *

* * @@ -270,38 +271,9 @@ import java.security.Permission; * *

Note on wildcards: In an object name pattern, a path element * of exactly ** corresponds to a meta - * wildcard that will match any number of sub namespaces. Hence:

- *
    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    patternmatchesdoesn't match
    **//D:k=va//D:k=v
    - * a//b//D:k=v
    - * a//b//c//D:k=v
    D:k=v
    a//**//D:k=va//b//D:k=v
    - * a//b//c//D:k=v
    b//b//c//D:k=v
    - * a//D:k=v
    - * D:k=v
    a//**//e//D:k=va//b//e//D:k=v
    - * a//b//c//e//D:k=v
    a//b//c//c//D:k=v
    - * b//b//c//e//D:k=v
    - * a//e//D:k=v
    - * e//D:k=v
    a//b**//e//D:k=va//b//e//D:k=va//b//c//e//D:k=v
    - * because in that case b**
    - * is not a meta-wildcard - and b**
    - * is thus equivalent to b*.
    - *
+ * wildcard that will match any number of sub namespaces. + * See ObjectName documentation + * for more details.

* *

If {@code ::} is omitted, then one of * member or object name may be omitted. diff --git a/src/share/classes/javax/management/namespace/JMXNamespaces.java b/src/share/classes/javax/management/namespace/JMXNamespaces.java index f19dfa570..ca7244abd 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespaces.java +++ b/src/share/classes/javax/management/namespace/JMXNamespaces.java @@ -292,17 +292,13 @@ public class JMXNamespaces { if (path == null || to == null) throw new IllegalArgumentException("Null argument"); checkTrailingSlashes(path); - try { - String prefix = path; - if (!prefix.equals("")) prefix = - ObjectNameRouter.normalizeNamespacePath( + String prefix = path; + if (!prefix.equals("")) + prefix = ObjectNameRouter.normalizeNamespacePath( prefix + NAMESPACE_SEPARATOR,false,false,false); - return to.withDomain( + return to.withDomain( ObjectNameRouter.normalizeDomain( prefix+to.getDomain(),false)); - } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(path+": "+x,x); - } } /** diff --git a/src/share/classes/javax/management/namespace/package-info.java b/src/share/classes/javax/management/namespace/package-info.java index df8354bab..5c014286d 100644 --- a/src/share/classes/javax/management/namespace/package-info.java +++ b/src/share/classes/javax/management/namespace/package-info.java @@ -204,7 +204,38 @@ * * An easier way to access MBeans contained in a name space is to * cd inside the name space, as shown in the following paragraph. - *

+ *

+ * Although ObjectName patterns where the characters + * * and ? appear in the namespace path are + * legal, they are not valid in the {@code name} parameter of the + * MBean Server {@link + * javax.management.MBeanServer#queryNames queryNames} and {@link + * javax.management.MBeanServer#queryMBeans queryMBeans} methods.
+ * When invoking queryNames or queryMBeans, + * only ObjectNames of the form:
+ * [namespace-without-pattern//]*[pattern-without-namespace]:key-properties-with-or-without-pattern + * are valid.
+ * In other words: in the case of {@link + * javax.management.MBeanServer#queryNames queryNames} and {@link + * javax.management.MBeanServer#queryMBeans queryMBeans}, if a + * namespace path is present, it must not contain any pattern. + *

+ * There is no such restriction for the {@code query} parameter of these + * methods. However, it must be noted that the {@code query} parameter + * will be evaluated in the context of the namespace where the MBeans + * selected by the pattern specified in {@code name} are located. + * This means that if {@code query} parameter is an ObjectName pattern that + * contains a namespace path, no MBean name will match and the result of + * the query will be empty.
+ * In other words:

+ *
  • {@code queryNames("foo//bar//?a?:*","b?z:type=Monitor,*")} will select + * all MBeans in namespace foo//bar whose names match both + * ?a?:* and b?z:type=Monitor,*, but
  • + *
  • {@code queryNames("foo//bar//?a?:*","foo//bar//b?z:type=Monitor,*")} + * will select nothing because no name matching ?a?:* will + * also match foo//bar//b?z:type=Monitor,*. + *
  • + *
* *

Narrowing Down Into a Name Spaces

*

@@ -228,7 +259,8 @@ * to name space {@code "foo"} behaves just like a regular MBean server. * However, it may sometimes throw an {@link * java.lang.UnsupportedOperationException UnsupportedOperationException} - * wrapped in a JMX exception if you try to call an operation which is not + * wrapped in a {@link javax.management.RuntimeOperationsException + * RuntimeOperationsException} if you try to call an operation which is not * supported by the underlying name space handler. *
For instance, {@link javax.management.MBeanServer#registerMBean * registerMBean} is not supported for name spaces mounted from remote diff --git a/test/javax/management/namespace/LeadingSeparatorsTest.java b/test/javax/management/namespace/LeadingSeparatorsTest.java index 5660b2753..6f931ffd4 100644 --- a/test/javax/management/namespace/LeadingSeparatorsTest.java +++ b/test/javax/management/namespace/LeadingSeparatorsTest.java @@ -24,7 +24,7 @@ * @test LeadingSeparatorsTest.java * @summary Test that the semantics of a leading // in ObjectName is respected. * @author Daniel Fuchs - * @bug 5072476 + * @bug 5072476 6768935 * @run clean LeadingSeparatorsTest Wombat WombatMBean * @compile -XDignore.symbol.file=true LeadingSeparatorsTest.java * @run build LeadingSeparatorsTest Wombat WombatMBean @@ -36,6 +36,7 @@ import java.util.Arrays; import java.util.Set; import java.util.HashSet; import java.util.logging.Logger; +import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.NotCompliantMBeanException; @@ -121,19 +122,29 @@ public class LeadingSeparatorsTest { // register wombat using an object name with a leading // final Object obj = new MyWombat(); // check that returned object name doesn't have the leading // - assertEquals(n2,top.registerMBean(obj, n1).getObjectName()); + assertEquals(n2,top.registerMBean(obj, n2).getObjectName()); System.out.println(n1+" registered"); // check that the registered Wombat can be accessed with all its // names. System.out.println(n2+" mood is: "+top.getAttribute(n2, "Mood")); - System.out.println(n1+" mood is: "+top.getAttribute(n1, "Mood")); + try { + System.out.println(n1+" mood is: "+top.getAttribute(n1, "Mood")); + throw new Exception("Excepected exception not thrown for "+n1); + } catch (InstanceNotFoundException x) { + System.out.println("OK: "+x); + } System.out.println(n4+" mood is: "+top.getAttribute(n4, "Mood")); - System.out.println(n3+" mood is: "+top.getAttribute(n3, "Mood")); + try { + System.out.println(n3+" mood is: "+top.getAttribute(n3, "Mood")); + throw new Exception("Excepected exception not thrown for "+n3); + } catch (InstanceNotFoundException x) { + System.out.println("OK: "+x); + } // call listMatching. The result should not contain any prefix. final Set res = (Set) - top.invoke(n3, "listMatching", + top.invoke(n4, "listMatching", // remove rmi// from rmi//*:* JMXNamespaces.deepReplaceHeadNamespace( new Object[] {ObjectName.WILDCARD.withDomain("rmi//*")}, @@ -158,7 +169,7 @@ public class LeadingSeparatorsTest { // //niark//niark// // final Set res4 = (Set) - top.invoke(n3, "untrue", + top.invoke(n4, "untrue", // remove niark//niark : should remove nothing since // our ObjectName begins with a leading // JMXNamespaces.deepReplaceHeadNamespace( diff --git a/test/javax/management/namespace/NullDomainObjectNameTest.java b/test/javax/management/namespace/NullDomainObjectNameTest.java index 6b1ad51c2..fa8455e2a 100644 --- a/test/javax/management/namespace/NullDomainObjectNameTest.java +++ b/test/javax/management/namespace/NullDomainObjectNameTest.java @@ -40,6 +40,7 @@ import javax.management.MBeanServerFactory; import javax.management.NotCompliantMBeanException; import javax.management.ObjectInstance; import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXRemoteNamespace; import javax.management.namespace.JMXNamespace; @@ -215,9 +216,20 @@ public class NullDomainObjectNameTest { assertEquals(proxy.queryNames( new ObjectName(":*"),null). contains(moi3.getObjectName()),true); - failed("queryNames(null,null) should have failed for faked//"); - } catch (IllegalArgumentException x) { - System.out.println("Received expected exception for faked//: "+x); + failed("queryNames(new ObjectName(\":*\"),null) " + + "should have failed for faked//"); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) + System.out.println( + "Received expected exception for faked//: "+x); + else { + System.err.println( + "Expected exception has unexpected cause " + + "for faked//: "+x.getCause()); + x.printStackTrace(); + failed("queryNames(new ObjectName(\":*\"),null)" + + " failed with unexpected cause for faked//"); + } } // These should fail because the ObjectName doesn't start // with "faked//" @@ -226,9 +238,20 @@ public class NullDomainObjectNameTest { "new ObjectName(\":*\"),null) with faked//"); assertEquals(proxy.queryMBeans( new ObjectName(":*"),null).contains(moi3),true); - failed("queryMBeans(null,null) should have failed for faked//"); - } catch (IllegalArgumentException x) { - System.out.println("Received expected exception for faked//: "+x); + failed("queryMBeans(new ObjectName(\":*\"),null)" + + " should have failed for faked//"); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) + System.out.println( + "Received expected exception for faked//: "+x); + else { + System.err.println( + "Expected exception has unexpected cause " + + "for faked//: "+x.getCause()); + x.printStackTrace(); + failed("queryMBeans(new ObjectName(\":*\"),null) " + + "failed with unexpected cause for faked//"); + } } System.out.println("Checking queryNames(faked//*:*,null)"); diff --git a/test/javax/management/namespace/NullObjectNameTest.java b/test/javax/management/namespace/NullObjectNameTest.java index 0e645dc1e..5cf99a230 100644 --- a/test/javax/management/namespace/NullObjectNameTest.java +++ b/test/javax/management/namespace/NullObjectNameTest.java @@ -41,6 +41,7 @@ import javax.management.MBeanServerFactory; import javax.management.NotCompliantMBeanException; import javax.management.ObjectInstance; import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXRemoteNamespace; import javax.management.namespace.JMXNamespace; @@ -189,15 +190,35 @@ public class NullObjectNameTest { assertEquals(proxy.queryNames(null,null). contains(moi3.getObjectName()),true); failed("queryNames(null,null) should have failed for faked//"); - } catch (IllegalArgumentException x) { - System.out.println("Received expected exception for faked//: "+x); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) + System.out.println( + "Received expected exception for faked//: "+x); + else { + System.err.println( + "Expected exception has unexpected cause " + + "for faked//: "+x.getCause()); + x.printStackTrace(); + failed("queryNames(null,null) failed with unexpected " + + "cause for faked//"); + } } try { System.out.println("Checking queryMBeans(null,null) with faked//"); assertEquals(proxy.queryMBeans(null,null).contains(moi3),true); failed("queryMBeans(null,null) should have failed for faked//"); - } catch (IllegalArgumentException x) { - System.out.println("Received expected exception for faked//: "+x); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) + System.out.println( + "Received expected exception for faked//: "+x); + else { + System.err.println( + "Expected exception has unexpected cause " + + "for faked//: "+x.getCause()); + x.printStackTrace(); + failed("queryMBeans(null,null) failed with unexpected " + + "cause for faked//"); + } } System.out.println("Checking queryNames(faked//*:*,null)"); assertEquals(proxy.queryNames(new ObjectName("faked//*:*"),null). diff --git a/test/javax/management/namespace/QueryNamesTest.java b/test/javax/management/namespace/QueryNamesTest.java index 1af597ace..5b5076446 100644 --- a/test/javax/management/namespace/QueryNamesTest.java +++ b/test/javax/management/namespace/QueryNamesTest.java @@ -25,7 +25,7 @@ * @test QueryNamesTest.java 1.4 * @summary Test how queryNames works with Namespaces. * @author Daniel Fuchs - * @bug 5072476 + * @bug 5072476 6768935 * @run clean QueryNamesTest Wombat WombatMBean * @run build QueryNamesTest Wombat WombatMBean * @run main QueryNamesTest @@ -34,6 +34,7 @@ import java.io.IOException; import java.lang.management.ManagementFactory; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -52,7 +53,9 @@ import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespaces; @@ -366,6 +369,66 @@ public class QueryNamesTest { return res; } + private static void checkNsPattern(MBeanServer server) throws Exception { + final List list = new ArrayList(); + for (String s : namespaces) { + final String[] cmpnt = s.split("//"); + for (int i=0;i res = new HashSet(); + + try { + res.addAll(server.queryNames(ObjectName.valueOf(s),null)); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) { + System.out.println("queryNames("+s+"): OK - received "+x.getCause()); + continue; + } + System.err.println("queryNames("+s+"): Bad cause: "+x.getCause()); + throw x; + } catch (Exception x) { + System.err.println("queryNames("+s+"): Bad exception: "+x); + throw x; + } + System.err.println("queryNames("+s+"): Bad result: "+res); + System.err.println("queryNames("+s+"): Excpected exception not thrown."); + throw new Exception("queryNames("+s+"): Excpected exception not thrown."); + } + for (String s : list) { + final Set res = new HashSet(); + + try { + res.addAll(server.queryMBeans(ObjectName.valueOf(s),null)); + } catch (RuntimeOperationsException x) { + if (x.getCause() instanceof IllegalArgumentException) { + System.out.println("queryMBeans("+s+"): OK - received "+x.getCause()); + continue; + } + System.err.println("queryMBeans("+s+"): Bad cause: "+x.getCause()); + throw x; + } catch (Exception x) { + System.err.println("queryMBeans("+s+"): Bad exception: "+x); + throw x; + } + System.err.println("queryMBeans("+s+"): Bad result: "+res); + System.err.println("queryMBeans("+s+"): Excpected exception not thrown."); + throw new Exception("queryMBeans("+s+"): Excpected exception not thrown."); + } + } + public static void main(String[] args) throws Exception { final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); @@ -378,6 +441,7 @@ public class QueryNamesTest { System.out.println("Domains: " +Arrays.asList(server.getDomains())); checkRegistration(server); checkNsQuery(server); + checkNsPattern(server); } finally { boolean res = true; res = res && removeWombats(server, wombats); -- GitLab