From bbac59f2184ef5477a2ac0b75229da546ca13c95 Mon Sep 17 00:00:00 2001
From: Eamonn McManus
Date: Fri, 7 Nov 2008 19:19:08 +0100
Subject: [PATCH] 6336968: Methods to convert AttributeList to/from Map
6750008: Add JMX.getSpecificationVersion(MBeanServerConnection) and document
interop 6750472: Add a way to convert a CompositeData into a Map 6752563:
Allow CompositeDataSupport to have zero items
Small JMX RFEs
Reviewed-by: dfuchs
---
.../javax/management/AttributeList.java | 206 +++++++-----
.../share/classes/javax/management/JMX.java | 76 +++++
.../management/MBeanServerConnection.java | 96 +++++-
.../management/MBeanServerNotification.java | 27 ++
.../management/QueryNotificationFilter.java | 6 +-
.../openmbean/CompositeDataSupport.java | 314 ++++++++++--------
.../classes/javax/management/package.html | 226 +++++++++----
.../MBeanServer/AttributeListMapTest.java | 115 +++++++
.../AttributeListTypeSafeTest.java | 109 ++++++
.../openmbean/CompositeDataToMapTest.java | 116 +++++++
.../mandatory/version/JMXSpecVersionTest.java | 308 +++++++++++++++++
11 files changed, 1302 insertions(+), 297 deletions(-)
create mode 100644 jdk/test/javax/management/MBeanServer/AttributeListMapTest.java
create mode 100644 jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java
create mode 100644 jdk/test/javax/management/openmbean/CompositeDataToMapTest.java
create mode 100644 jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java
diff --git a/jdk/src/share/classes/javax/management/AttributeList.java b/jdk/src/share/classes/javax/management/AttributeList.java
index 1ce3004a337..a629d57ccd4 100644
--- a/jdk/src/share/classes/javax/management/AttributeList.java
+++ b/jdk/src/share/classes/javax/management/AttributeList.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 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
@@ -27,17 +27,23 @@ package javax.management;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
- * Represents a list of values for attributes of an MBean. The methods
- * used for the insertion of {@link javax.management.Attribute
- * Attribute} objects in the AttributeList overrides the
- * corresponding methods in the superclass
- * ArrayList. This is needed in order to insure that the
- * objects contained in the AttributeList are only
- * Attribute objects. This avoids getting an exception
- * when retrieving elements from the AttributeList.
+ *
Represents a list of values for attributes of an MBean. See the
+ * {@link MBeanServerConnection#getAttributes getAttributes} and
+ * {@link MBeanServerConnection#setAttributes setAttributes} methods of
+ * {@link MBeanServer} and {@link MBeanServerConnection}.
+ *
+ *
For compatibility reasons, it is possible, though
+ * highly discouraged, to add objects to an {@code AttributeList} that are
+ * not instances of {@code Attribute}. However, an {@code AttributeList}
+ * can be made type-safe, which means that an attempt to add
+ * an object that is not an {@code Attribute} will produce an {@code
+ * IllegalArgumentException}. An {@code AttributeList} becomes type-safe
+ * when the method {@link #asList()} is called on it.
*
* @since 1.5
*/
@@ -58,8 +64,8 @@ import java.util.List;
*/
public class AttributeList extends ArrayList
*
+ *
Because this class was introduced in version 2.0 of the JMX API,
+ * it may not be present on a remote JMX agent that is running an earlier
+ * version. The method {@link JMX#getSpecificationVersion
+ * JMX.getSpecificationVersion} can be used to determine the remote version.
+ *
*
This class uses the {@linkplain Query Query API} to specify the
* filtering logic. For example, to select only notifications where the
* {@linkplain Notification#getType() type} is {@code "com.example.mytype"},
diff --git a/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java b/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java
index 4ef93b47611..bd7c77af9b4 100644
--- a/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java
+++ b/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java
@@ -33,12 +33,14 @@ import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
// jmx import
+import java.util.TreeSet;
//
@@ -60,16 +62,15 @@ public class CompositeDataSupport
* respective values.
* A {@link SortedMap} is used for faster retrieval of elements.
*/
- private SortedMap contents = new TreeMap();
+ private final SortedMap contents;
/**
* @serial The composite type of this composite data instance.
*/
- private CompositeType compositeType;
+ private final CompositeType compositeType;
/**
- *
- * Constructs a CompositeDataSupport instance with the specified
+ *
Constructs a CompositeDataSupport instance with the specified
* compositeType, whose item values
* are specified by itemValues[], in the same order as in
* itemNames[].
@@ -79,103 +80,67 @@ public class CompositeDataSupport
* The items contained in this CompositeDataSupport instance are
* internally stored in a TreeMap,
* thus sorted in ascending lexicographic order of their names, for faster
- * retrieval of individual item values.
- *
- * The constructor checks that all the constraints listed below for each
+ * retrieval of individual item values.
+ *
+ *
The constructor checks that all the constraints listed below for each
* parameter are satisfied,
- * and throws the appropriate exception if they are not.
- *
- * @param compositeType the composite type of this composite
- * data instance;
- * must not be null.
- *
- * @param itemNames itemNames must list, in any order, all the
- * item names defined in compositeType;
- * the order in which the names are listed, is used to
- * match values in itemValues[];
- * must not be null or empty.
- *
- * @param itemValues the values of the items, listed in the same order as
- * their respective names in itemNames;
- * each item value can be null, but if it is non-null it must be
- * a valid value for the open type defined in compositeType for the corresponding item;
- * must be of the same size as itemNames; must not be null or empty.
- *
- * @throws IllegalArgumentException compositeType is null, or itemNames[] or itemValues[] is null or empty,
- * or one of the elements in itemNames[] is a null or empty string,
- * or itemNames[] and itemValues[] are not of the same size.
- *
- * @throws OpenDataException itemNames[] or itemValues[]'s size differs from
- * the number of items defined in compositeType,
- * or one of the elements in itemNames[] does not exist as an item name defined in compositeType,
- * or one of the elements in itemValues[] is not a valid value for the corresponding item
- * as defined in compositeType.
- *
+ * and throws the appropriate exception if they are not.
+ *
+ * @param compositeType the composite type of this composite
+ * data instance; must not be null.
+ *
+ * @param itemNames itemNames must list, in any order, all the
+ * item names defined in compositeType; the order in which the
+ * names are listed, is used to match values in itemValues[]; must
+ * not be null.
+ *
+ * @param itemValues the values of the items, listed in the same order as
+ * their respective names in itemNames; each item value can be
+ * null, but if it is non-null it must be a valid value for the open type
+ * defined in compositeType for the corresponding item; must be of
+ * the same size as itemNames; must not be null.
+ *
+ * @throws IllegalArgumentException compositeType is null, or
+ * itemNames[] or itemValues[] is null or empty, or one
+ * of the elements in itemNames[] is a null or empty string, or
+ * itemNames[] and itemValues[] are not of the same size.
+ *
+ * @throws OpenDataException itemNames[] or
+ * itemValues[]'s size differs from the number of items defined in
+ * compositeType, or one of the elements in itemNames[]
+ * does not exist as an item name defined in compositeType, or one
+ * of the elements in itemValues[] is not a valid value for the
+ * corresponding item as defined in compositeType.
*/
- public CompositeDataSupport(CompositeType compositeType, String[] itemNames, Object[] itemValues)
- throws OpenDataException {
+ public CompositeDataSupport(
+ CompositeType compositeType, String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
+ this(makeMap(itemNames, itemValues), compositeType);
+ }
- // Check compositeType is not null
- //
- if (compositeType == null) {
- throw new IllegalArgumentException("Argument compositeType cannot be null.");
- }
+ private static SortedMap makeMap(
+ String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
- // item names defined in compositeType:
- Set namesSet = compositeType.keySet();
-
- // Check the array itemNames is not null or empty (length!=0) and
- // that there is no null element or empty string in it
- //
- checkForNullElement(itemNames, "itemNames");
- checkForEmptyString(itemNames, "itemNames");
-
- // Check the array itemValues is not null or empty (length!=0)
- // (NOTE: we allow null values as array elements)
- //
- if ( (itemValues == null) || (itemValues.length == 0) ) {
- throw new IllegalArgumentException("Argument itemValues[] cannot be null or empty.");
- }
-
- // Check that the sizes of the 2 arrays itemNames and itemValues are the same
- //
+ if (itemNames == null || itemValues == null)
+ throw new IllegalArgumentException("Null itemNames or itemValues");
if (itemNames.length != itemValues.length) {
- throw new IllegalArgumentException("Array arguments itemNames[] and itemValues[] "+
- "should be of same length (got "+ itemNames.length +
- " and "+ itemValues.length +").");
+ throw new IllegalArgumentException(
+ "Different lengths: itemNames[" + itemNames.length +
+ "], itemValues[" + itemValues.length + "]");
}
- // Check the size of the 2 arrays is equal to the number of items defined in compositeType
- //
- if (itemNames.length != namesSet.size()) {
- throw new OpenDataException("The size of array arguments itemNames[] and itemValues[] should be equal to the number of items defined"+
- " in argument compositeType (found "+ itemNames.length +" elements in itemNames[] and itemValues[],"+
- " expecting "+ namesSet.size() +" elements according to compositeType.");
+ SortedMap map = new TreeMap();
+ for (int i = 0; i < itemNames.length; i++) {
+ String name = itemNames[i];
+ if (name == null || name.equals(""))
+ throw new IllegalArgumentException("Null or empty item name");
+ if (map.containsKey(name))
+ throw new OpenDataException("Duplicate item name " + name);
+ map.put(itemNames[i], itemValues[i]);
}
- // Check parameter itemNames[] contains all names defined in the compositeType of this instance
- //
- if ( ! Arrays.asList(itemNames).containsAll(namesSet) ) {
- throw new OpenDataException("Argument itemNames[] does not contain all names defined in the compositeType of this instance.");
- }
-
- // Check each element of itemValues[], if not null, is of the open type defined for the corresponding item
- //
- OpenType> itemType;
- for (int i=0; iitems.
* This constructor converts the keys to a string array and the values to an object array and calls
* CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[]).
- *
+ *
* @param compositeType the composite type of this composite data instance;
* must not be null.
- *
* @param items the mappings of all the item names to their values;
* items must contain all the item names defined in compositeType;
- * must not be null or empty.
- *
- * @throws IllegalArgumentException compositeType is null, or items is null or empty,
- * or one of the keys in items is a null or empty string,
- * or one of the values in items is null.
- *
- * @throws OpenDataException items' size differs from the number of items defined in compositeType,
- * or one of the keys in items does not exist as an item name defined in compositeType,
- * or one of the values in items is not a valid value for the corresponding item
- * as defined in compositeType.
- *
- * @throws ArrayStoreException one or more keys in items is not of the class java.lang.String.
- *
+ * must not be null.
+ *
+ * @throws IllegalArgumentException compositeType is null, or
+ * items is null, or one of the keys in items is a null
+ * or empty string.
+ * @throws OpenDataException items' size differs from the
+ * number of items defined in compositeType, or one of the
+ * keys in items does not exist as an item name defined in
+ * compositeType, or one of the values in items
+ * is not a valid value for the corresponding item as defined in
+ * compositeType.
+ * @throws ArrayStoreException one or more keys in items is not of
+ * the class java.lang.String.
+ *
+ * @see #toMap
*/
public CompositeDataSupport(CompositeType compositeType,
Map items)
throws OpenDataException {
+ this(makeMap(items), compositeType);
+ }
+ private static SortedMap makeMap(Map items) {
+ if (items == null)
+ throw new IllegalArgumentException("Null items map");
+ if (items.containsKey(null) || items.containsKey(""))
+ throw new IllegalArgumentException("Null or empty item name");
- // Let the other constructor do the job, as the call to another constructor must be the first call
+ SortedMap map = new TreeMap();
+ for (Object key : items.keySet()) {
+ if (!(key instanceof String)) {
+ throw new ArrayStoreException("Item name is not string: " + key);
+ // This can happen because of erasure. The particular
+ // exception is a historical artifact - an implementation
+ // detail that leaked into the API.
+ }
+ map.put((String) key, items.get(key));
+ }
+ return map;
+ }
+
+ private CompositeDataSupport(
+ SortedMap items, CompositeType compositeType)
+ throws OpenDataException {
+
+ // Check compositeType is not null
//
- this( compositeType,
- (items==null ? null : items.keySet().toArray(new String[items.size()])), // may raise an ArrayStoreException
- (items==null ? null : items.values().toArray()) );
- }
-
- /**
- *
- */
- private static void checkForNullElement(Object[] arg, String argName) {
- if ( (arg == null) || (arg.length == 0) ) {
- throw new IllegalArgumentException(
- "Argument "+ argName +"[] cannot be null or empty.");
+ if (compositeType == null) {
+ throw new IllegalArgumentException("Argument compositeType cannot be null.");
}
- for (int i=0; i namesFromType = compositeType.keySet();
+ Set namesFromItems = items.keySet();
+
+ // This is just a comparison, but we do it this way for a better
+ // exception message.
+ if (!namesFromType.equals(namesFromItems)) {
+ Set extraFromType = new TreeSet(namesFromType);
+ extraFromType.removeAll(namesFromItems);
+ Set extraFromItems = new TreeSet(namesFromItems);
+ extraFromItems.removeAll(namesFromType);
+ if (!extraFromType.isEmpty() || !extraFromItems.isEmpty()) {
+ throw new OpenDataException(
+ "Item names do not match CompositeType: " +
+ "names in items but not in CompositeType: " + extraFromItems +
+ "; names in CompositeType but not in items: " + extraFromType);
}
}
- }
- /**
- *
- */
- private static void checkForEmptyString(String[] arg, String argName) {
- for (int i=0; i itemType = compositeType.getType(name);
+ if (!itemType.isValue(value)) {
+ throw new OpenDataException(
+ "Argument value of wrong type for item " + name +
+ ": value " + value + ", type " + itemType);
+ }
}
}
+
+ // Initialize internal fields: compositeType and contents
+ //
+ this.compositeType = compositeType;
+ this.contents = items;
}
/**
@@ -328,6 +328,54 @@ public class CompositeDataSupport
return Collections.unmodifiableCollection(contents.values());
}
+ /**
+ *
Returns a Map representing the contents of the given CompositeData.
+ * Each item in the CompositeData is represented by an entry in the map,
+ * where the name and value of the item are the key and value of the entry.
+ * The returned value is modifiable but modifications to it have no effect
+ * on the original CompositeData.
+ *
+ *
For example, if you have a CompositeData {@code cd1} and you want
+ * to produce another CompositeData {@code cd2} which is the same except
+ * that the value of its {@code id} item has been changed to 253, you
+ * could write:
Logically, this method would be a method in the {@link CompositeData}
+ * interface, but cannot be for compatibility reasons.
+ *
+ * @param cd the CompositeData to convert to a Map.
+ *
+ * @return a Map that is a copy of the contents of {@code cd}.
+ *
+ * @throws IllegalArgumentException if {@code cd} is null.
+ *
+ * @see #CompositeDataSupport(CompositeType, Map)
+ */
+ public static Map toMap(CompositeData cd) {
+ if (cd == null)
+ throw new IllegalArgumentException("Null argument");
+
+ // If we really wanted, we could check whether cd is a
+ // CompositeDataSupport and return a copy of cd.contents if so,
+ // but I don't think that would be substantially faster.
+ Map map = new LinkedHashMap();
+ CompositeType ct = cd.getCompositeType();
+ for (String key : ct.keySet()) {
+ Object value = cd.get(key);
+ map.put(key, value);
+ }
+ return map;
+ }
+
/**
* Compares the specified obj parameter with this
* CompositeDataSupport instance for equality.
diff --git a/jdk/src/share/classes/javax/management/package.html b/jdk/src/share/classes/javax/management/package.html
index 14027d648ea..372c30efd9f 100644
--- a/jdk/src/share/classes/javax/management/package.html
+++ b/jdk/src/share/classes/javax/management/package.html
@@ -1,7 +1,7 @@
-
-javax.management package
-
-
-
-
Provides the core classes for the Java Management Extensions.
+ -->
+
+
+
Provides the core classes for the Java Management Extensions.
-
The Java Management Extensions
- (JMXTM) API is a standard
- API for management and monitoring. Typical uses include:
+
The Java Management Extensions
+ (JMXTM) API is a standard
+ API for management and monitoring. Typical uses include:
-
-
consulting and changing application configuration
+
+
consulting and changing application configuration
-
accumulating statistics about application behavior and
- making them available
+
accumulating statistics about application behavior and
+ making them available
-
notifying of state changes and erroneous conditions.
-
+
notifying of state changes and erroneous conditions.
+
-
The JMX API can also be used as part of a solution for
- managing systems, networks, and so on.
+
The JMX API can also be used as part of a solution for
+ managing systems, networks, and so on.
-
The API includes remote access, so a remote management
- program can interact with a running application for these
- purposes.
+
The API includes remote access, so a remote management
+ program can interact with a running application for these
+ purposes.
-
MBeans
+
MBeans
-
The fundamental notion of the JMX API is the MBean.
- An MBean is a named managed object representing a
- resource. It has a management interface consisting
- of:
+
The fundamental notion of the JMX API is the MBean.
+ An MBean is a named managed object representing a
+ resource. It has a management interface consisting
+ of:
named and typed attributes that can be read and/or
@@ -92,40 +92,40 @@ have any questions.
public interface ConfigurationMBean {
- public int getCacheSize();
- public void setCacheSize(int size);
- public long getLastChangedTime();
- public void save();
+ public int getCacheSize();
+ public void setCacheSize(int size);
+ public long getLastChangedTime();
+ public void save();
}
-
+
-
The methods getCacheSize and
- setCacheSize define a read-write attribute of
- type int called CacheSize (with an
- initial capital, unlike the JavaBeans convention).
+
The methods getCacheSize and
+ setCacheSize define a read-write attribute of
+ type int called CacheSize (with an
+ initial capital, unlike the JavaBeans convention).
-
The method getLastChangedTime defines an
- attribute of type long called
- LastChangedTime. This is a read-only attribute,
- since there is no method setLastChangedTime.
+
The method getLastChangedTime defines an
+ attribute of type long called
+ LastChangedTime. This is a read-only attribute,
+ since there is no method setLastChangedTime.
-
The method save defines an operation called
- save. It is not an attribute, since its name
- does not begin with get, set, or
- is.
+
The method save defines an operation called
+ save. It is not an attribute, since its name
+ does not begin with get, set, or
+ is.
-
The exact naming patterns for Standard MBeans are detailed in
- the JMX Specification.
+
The exact naming patterns for Standard MBeans are detailed in
+ the JMX Specification.
-
There are two ways to make a Java object that is an MBean
- with this management interface. One is for the object to be
- of a class that has exactly the same name as the Java
- interface but without the MBean suffix. So in
- the example the object would be of the class
- Configuration, in the same Java package as
- ConfigurationMBean. The second way is to use the
- {@link javax.management.StandardMBean StandardMBean}
- class.
+
There are two ways to make a Java object that is an MBean
+ with this management interface. One is for the object to be
+ of a class that has exactly the same name as the Java
+ interface but without the MBean suffix. So in
+ the example the object would be of the class
+ Configuration, in the same Java package as
+ ConfigurationMBean. The second way is to use the
+ {@link javax.management.StandardMBean StandardMBean}
+ class.
Defining Standard MBeans with annotations
@@ -272,37 +272,37 @@ have any questions.
int cacheSize = mbs.getAttribute(name, "CacheSize");
{@link javax.management.Attribute Attribute} newCacheSize =
- new Attribute("CacheSize", new Integer(2000));
+ new Attribute("CacheSize", new Integer(2000));
mbs.setAttribute(name, newCacheSize);
mbs.invoke(name, "save", new Object[0], new Class[0]);
-
+
Alternatively, if you have a Java interface that
corresponds to the management interface for the MBean, you can use an
MBean proxy like this:
Using an MBean proxy is just a convenience. The second
- example ends up calling the same MBeanServer
- operations as the first one.
+
Using an MBean proxy is just a convenience. The second
+ example ends up calling the same MBeanServer
+ operations as the first one.
-
An MBean Server can be queried for MBeans whose names match
- certain patterns and/or whose attributes meet certain
- constraints. Name patterns are constructed using the {@link
- javax.management.ObjectName ObjectName} class and constraints
- are constructed using the {@link javax.management.Query Query}
- class. The methods {@link
- javax.management.MBeanServer#queryNames queryNames} and {@link
- javax.management.MBeanServer#queryMBeans queryMBeans} then
- perform the query.
+
An MBean Server can be queried for MBeans whose names match
+ certain patterns and/or whose attributes meet certain
+ constraints. Name patterns are constructed using the {@link
+ javax.management.ObjectName ObjectName} class and constraints
+ are constructed using the {@link javax.management.Query Query}
+ class. The methods {@link
+ javax.management.MBeanServer#queryNames queryNames} and {@link
+ javax.management.MBeanServer#queryMBeans queryMBeans} then
+ perform the query.
MBean lifecycle and resource injection
@@ -407,6 +407,92 @@ have any questions.
So for example an SNMP GET operation might result in a
getAttribute on the MBean Server.
+
Interoperability between versions of the JMX
+ specification
+
+
When a client connects to a server using the JMX Remote
+ API, it is possible that they do not have the same version
+ of the JMX specification. The version of the JMX
+ specification described here is version 2.0. Previous
+ versions were 1.0, 1.1, 1.2, and 1.4. (There was no 1.3.)
+ The standard JMX Remote API is defined to work with version
+ 1.2 onwards, so in standards-based deployment the only
+ interoperability questions that arise concern version 1.2
+ onwards.
+
+
Every version of the JMX specification continues to
+ implement the features of previous versions. So when the
+ client is running an earlier version than the server, there
+ should not be any interoperability concerns. The only
+ exception is the unlikely one where a pre-2.0 client used
+ the string {@code //} in the domain part of an {@link
+ javax.management.ObjectName ObjectName}.
+
+
When the client is running a later version than the server,
+ certain newer features may not be available, as detailed in
+ the next sections. The method {@link
+ javax.management.JMX#getSpecificationVersion
+ JMX.getSpecificationVersion} can be used to determine the
+ server version to check if required features are
+ available.
+
+
If the remote MBean Server is 1.4
+
+
+
+
You cannot use {@link
+ javax.management.QueryNotificationFilter
+ QueryNotificationFilter} in {@link
+ javax.management.MBeanServerConnection#addNotificationListener
+ addNotificationListener} since this class did not exist
+ in 1.4.
+
+
In an attribute in a query, you cannot access values
+ inside complex types using dot syntax, for example
+ {@link javax.management.Query#attr Query.attr}{@code
+ ("HeapMemoryUsage.used")}.
+
+
The packages {@link javax.management.event} and
+ {@link javax.management.namespace} did not exist in 1.4,
+ so you cannot remotely create instances of the MBeans
+ they define.
+
+
Even if the remote MBean Server is 2.0, you cannot in
+ general suppose that {@link
+ javax.management.event.EventClient EventClient} or
+ {@link javax.management.ClientContext ClientContext}
+ will work there without first checking. If the remote
+ MBean Server is 1.4 then those checks will return false.
+ An attempt to use these features without checking will
+ fail in the same way as for a remote 2.0 that is not
+ configured to support them.
+
+
+
If the remote MBean Server is 1.2
+
+
In addition to the above,
+
+
+
+
You cannot use wildcards in a key property of an
+ {@link javax.management.ObjectName ObjectName}, for
+ example {@code domain:type=Foo,name=*}. Wildcards that
+ match whole properties are still allowed, for example
+ {@code *:*} or {@code *:type=Foo,*}.
+
+
You cannot use {@link
+ javax.management.Query#isInstanceOf Query.isInstanceOf}
+ in a query.
+
+
You cannot use dot syntax such as {@code
+ HeapMemoryUsage.used} in the {@linkplain
+ javax.management.monitor.Monitor#setObservedAttribute
+ observed attribute} of a monitor, as described in the
+ documentation for the {@link javax.management.monitor}
+ package.
+
+
+
@see
Java SE 6 Platform documentation on JMX technology
diff --git a/jdk/test/javax/management/MBeanServer/AttributeListMapTest.java b/jdk/test/javax/management/MBeanServer/AttributeListMapTest.java
new file mode 100644
index 00000000000..94dd39306a3
--- /dev/null
+++ b/jdk/test/javax/management/MBeanServer/AttributeListMapTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6336968
+ * @summary Test AttributeList.toMap
+ * @author Eamonn McManus
+ */
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+
+public class AttributeListMapTest {
+
+ private static String failure;
+
+ public static void main(String[] args) throws Exception {
+ AttributeList attrs = new AttributeList(Arrays.asList(
+ new Attribute("Str", "Five"),
+ new Attribute("Int", 5),
+ new Attribute("Flt", 5.0)));
+
+ Map map = attrs.toMap();
+ final Map expectMap = new HashMap();
+ for (Attribute attr : attrs.asList())
+ expectMap.put(attr.getName(), attr.getValue());
+ assertEquals("Initial map", expectMap, map);
+ assertEquals("Initial map size", 3, map.size());
+ assertEquals("Name set", expectMap.keySet(), map.keySet());
+ assertEquals("Values", new HashSet(expectMap.values()),
+ new HashSet(map.values()));
+ assertEquals("Entry set", expectMap.entrySet(), map.entrySet());
+
+ AttributeList attrs2 = new AttributeList(map);
+ assertEquals("AttributeList from Map", attrs, attrs2);
+ // This assumes that the Map conserves the order of the attributes,
+ // which is not specified but true because we use LinkedHashMap.
+
+ // Check that toMap fails if the list contains non-Attribute elements.
+ AttributeList attrs3 = new AttributeList(attrs);
+ attrs3.add("Hello"); // allowed but curious
+ try {
+ map = attrs3.toMap();
+ fail("toMap succeeded on list with non-Attribute elements");
+ } catch (Exception e) {
+ assertEquals("Exception for toMap with non-Atttribute elements",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ // Check that the Map does not reflect changes made to the list after
+ // the Map was obtained.
+ AttributeList attrs4 = new AttributeList(attrs);
+ map = attrs4.toMap();
+ attrs4.add(new Attribute("Big", new BigInteger("5")));
+ assertEquals("Map after adding element to list", expectMap, map);
+
+ // Check that if there is more than one Attribute with the same name
+ // then toMap() chooses the last of them.
+ AttributeList attrs5 = new AttributeList(attrs);
+ attrs5.add(new Attribute("Str", "Cinq"));
+ map = attrs5.toMap();
+ assertEquals("Size of Map for list with duplicate attribute name",
+ 3, map.size());
+ Object value = map.get("Str");
+ assertEquals("Value of Str in Map for list with two values for it",
+ "Cinq", value);
+
+ if (failure == null)
+ System.out.println("TEST PASSED");
+ else
+ throw new Exception("TEST FAILED: " + failure);
+ }
+
+ private static void assertEquals(String what, Object expect, Object actual) {
+ if (eq(expect, actual))
+ System.out.println("OK: " + what);
+ else
+ fail(what + ": expected " + expect + ", got " + actual);
+ }
+
+ private static boolean eq(Object x, Object y) {
+ return (x == null) ? (y == null) : x.equals(y);
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAIL: " + why);
+ failure = why;
+ }
+}
diff --git a/jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java b/jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java
new file mode 100644
index 00000000000..ed2fc96fd06
--- /dev/null
+++ b/jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6336968
+ * @summary Test adding non-Attribute values to an AttributeList.
+ * @author Eamonn McManus
+ */
+
+import java.util.Collections;
+import java.util.List;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+
+public class AttributeListTypeSafeTest {
+
+ private static String failure;
+
+ public static void main(String[] args) throws Exception {
+ // Test calling asList after adding non-Attribute by various means
+ for (Op op : Op.values()) {
+ AttributeList alist = new AttributeList();
+ alist.add(new Attribute("foo", "bar"));
+ doOp(alist, op);
+ String what = "asList() after calling " + op + " with non-Attribute";
+ try {
+ List lista = alist.asList();
+ fail(what + ": succeeded but should not have");
+ } catch (IllegalArgumentException e) {
+ System.out.println("OK: " + what + ": got IllegalArgumentException");
+ }
+ }
+
+ // Test adding non-Attribute by various means after calling asList
+ for (Op op : Op.values()) {
+ AttributeList alist = new AttributeList();
+ List lista = alist.asList();
+ lista.add(new Attribute("foo", "bar"));
+ String what = op + " with non-Attribute after calling asList()";
+ try {
+ doOp(alist, op);
+ fail(what + ": succeeded but should not have");
+ } catch (IllegalArgumentException e) {
+ System.out.println("OK: " + what + ": got IllegalArgumentException");
+ }
+ }
+
+ if (failure == null)
+ System.out.println("TEST PASSED");
+ else
+ throw new Exception("TEST FAILED: " + failure);
+ }
+
+ private static enum Op {
+ ADD("add(Object)"), ADD_AT("add(int, Object)"),
+ ADD_ALL("add(Collection)"), ADD_ALL_AT("add(int, Collection)"),
+ SET("set(int, Object)");
+
+ private Op(String what) {
+ this.what = what;
+ }
+
+ @Override
+ public String toString() {
+ return what;
+ }
+
+ private final String what;
+ }
+
+ private static void doOp(AttributeList alist, Op op) {
+ Object x = "oops";
+ switch (op) {
+ case ADD: alist.add(x); break;
+ case ADD_AT: alist.add(0, x); break;
+ case ADD_ALL: alist.add(Collections.singleton(x)); break;
+ case ADD_ALL_AT: alist.add(0, Collections.singleton(x)); break;
+ case SET: alist.set(0, x); break;
+ default: throw new AssertionError("Case not covered");
+ }
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAIL: " + why);
+ failure = why;
+ }
+
+}
diff --git a/jdk/test/javax/management/openmbean/CompositeDataToMapTest.java b/jdk/test/javax/management/openmbean/CompositeDataToMapTest.java
new file mode 100644
index 00000000000..30641b7c72f
--- /dev/null
+++ b/jdk/test/javax/management/openmbean/CompositeDataToMapTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6750472 6752563
+ * @summary Test CompositeDataSupport.toMap.
+ * @author Eamonn McManus
+ * @run main/othervm -ea CompositeDataToMapTest
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+public class CompositeDataToMapTest {
+ private static class IdentityInvocationHandler implements InvocationHandler {
+ private final Object wrapped;
+
+ public IdentityInvocationHandler(Object wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ public Object invoke(Object proxy, Method m, Object[] args)
+ throws Throwable {
+ try {
+ return m.invoke(wrapped, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ }
+
+ private static T wrap(T x, Class intf) {
+ InvocationHandler ih = new IdentityInvocationHandler(x);
+ return intf.cast(Proxy.newProxyInstance(
+ intf.getClassLoader(), new Class>[] {intf}, ih));
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (!CompositeDataToMapTest.class.desiredAssertionStatus())
+ throw new AssertionError("Must be run with -ea");
+
+ CompositeType emptyCT = new CompositeType(
+ "empty", "empty", new String[0], new String[0], new OpenType>[0]);
+ CompositeData emptyCD = new CompositeDataSupport(
+ emptyCT, Collections.emptyMap());
+ assert CompositeDataSupport.toMap(emptyCD).isEmpty() :
+ "Empty CD produces empty Map";
+
+ CompositeData emptyCD2 = new CompositeDataSupport(
+ emptyCT, new String[0], new Object[0]);
+ assert emptyCD.equals(emptyCD2) : "Empty CD can be constructed two ways";
+
+ CompositeType namedNumberCT = new CompositeType(
+ "NamedNumber", "NamedNumber",
+ new String[] {"name", "number"},
+ new String[] {"name", "number"},
+ new OpenType>[] {SimpleType.STRING, SimpleType.INTEGER});
+ Map namedNumberMap = new HashMap();
+ namedNumberMap.put("name", "Deich");
+ namedNumberMap.put("number", 10);
+ CompositeData namedNumberCD = new CompositeDataSupport(
+ namedNumberCT, namedNumberMap);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Map survives passage through CompositeData";
+
+ namedNumberCD = wrap(namedNumberCD, CompositeData.class);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Map survives passage through wrapped CompositeData";
+
+ namedNumberMap = CompositeDataSupport.toMap(namedNumberCD);
+ namedNumberMap.put("name", "Ceathar");
+ namedNumberMap.put("number", 4);
+ namedNumberCD = new CompositeDataSupport(namedNumberCT, namedNumberMap);
+ assert CompositeDataSupport.toMap(namedNumberCD).equals(namedNumberMap) :
+ "Modified Map survives passage through CompositeData";
+
+ try {
+ namedNumberMap = CompositeDataSupport.toMap(null);
+ assert false : "Null toMap arg provokes exception";
+ } catch (Exception e) {
+ assert e instanceof IllegalArgumentException :
+ "Exception for null toMap arg is IllegalArgumentException";
+ }
+ }
+}
diff --git a/jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java b/jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java
new file mode 100644
index 00000000000..69782690d0a
--- /dev/null
+++ b/jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6750008
+ * @summary Test JMX.getSpecificationVersion
+ * @author Eamonn McManus
+ */
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.ListIterator;
+import java.util.Set;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMX;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerDelegateMBean;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.StandardMBean;
+import javax.management.namespace.JMXNamespace;
+import javax.management.namespace.JMXNamespaces;
+import javax.management.namespace.MBeanServerSupport;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class JMXSpecVersionTest {
+ private static String failure;
+ private static final Object POISON_PILL = new Object();
+
+ private static class FakeDelegate implements DynamicMBean {
+ private final Object specVersion;
+ private final DynamicMBean delegate = new StandardMBean(
+ new MBeanServerDelegate(), MBeanServerDelegateMBean.class, false);
+
+ FakeDelegate(Object specVersion) {
+ this.specVersion = specVersion;
+ }
+
+ public Object getAttribute(String attribute)
+ throws AttributeNotFoundException, MBeanException,
+ ReflectionException {
+ if ("SpecificationVersion".equals(attribute)) {
+ if (specVersion == POISON_PILL)
+ throw new AttributeNotFoundException(attribute);
+ else
+ return specVersion;
+ } else
+ return delegate.getAttribute(attribute);
+ }
+
+ public void setAttribute(Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException,
+ MBeanException, ReflectionException {
+ delegate.setAttribute(attribute);
+ }
+
+ public AttributeList getAttributes(String[] attributes) {
+ AttributeList list = delegate.getAttributes(attributes);
+ for (ListIterator it = list.asList().listIterator();
+ it.hasNext(); ) {
+ Attribute attr = it.next();
+ if (attr.getName().equals("SpecificationVersion")) {
+ it.remove();
+ if (specVersion != POISON_PILL) {
+ attr = new Attribute(attr.getName(), specVersion);
+ it.add(attr);
+ }
+ }
+ }
+ return list;
+ }
+
+ public AttributeList setAttributes(AttributeList attributes) {
+ return delegate.setAttributes(attributes);
+ }
+
+ public Object invoke(String actionName, Object[] params,
+ String[] signature) throws MBeanException,
+ ReflectionException {
+ return delegate.invoke(actionName, params, signature);
+ }
+
+ public MBeanInfo getMBeanInfo() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }
+
+ private static class MBeanServerWithVersion extends MBeanServerSupport {
+ private final DynamicMBean delegate;
+
+ public MBeanServerWithVersion(Object specVersion) {
+ this.delegate = new FakeDelegate(specVersion);
+ }
+
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name)
+ throws InstanceNotFoundException {
+ if (MBeanServerDelegate.DELEGATE_NAME.equals(name))
+ return delegate;
+ else
+ throw new InstanceNotFoundException(name);
+ }
+
+ @Override
+ protected Set getNames() {
+ return Collections.singleton(MBeanServerDelegate.DELEGATE_NAME);
+ }
+ }
+
+ private static class EmptyMBeanServer extends MBeanServerSupport {
+ @Override
+ public DynamicMBean getDynamicMBeanFor(ObjectName name) throws InstanceNotFoundException {
+ throw new InstanceNotFoundException(name);
+ }
+
+ @Override
+ protected Set getNames() {
+ return Collections.emptySet();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
+ JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
+ url, null, mbs);
+ cs.start();
+
+ String realVersion = (String) mbs.getAttribute(
+ MBeanServerDelegate.DELEGATE_NAME, "SpecificationVersion");
+ assertEquals("Reported local version",
+ realVersion, JMX.getSpecificationVersion(mbs, null));
+ assertEquals("Reported local version >= \"2.0\"",
+ true, (realVersion.compareTo("2.0") >= 0));
+
+ JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress());
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+ assertEquals("Reported remote version",
+ realVersion, JMX.getSpecificationVersion(mbsc, null));
+
+ cc.close();
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(mbsc, null);
+ fail("JMX.getSpecificationVersion succeded over closed connection" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for closed connection",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(
+ new EmptyMBeanServer(), null);
+ fail("JMX.getSpecificationVersion succeded with empty MBean Server" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for empty MBean Server",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(null, null);
+ fail("JMX.getSpecificationVersion succeded with null MBean Server" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for null MBean Server",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ MBeanServer mbs1_2 = new MBeanServerWithVersion("1.2");
+ String version1_2 = JMX.getSpecificationVersion(mbs1_2, null);
+ assertEquals("Version for 1.2 MBean Server", "1.2", version1_2);
+
+ // It's completely nutty for an MBean Server to return null as the
+ // value of its spec version, and we don't actually say what happens
+ // in that case, but in fact we return the null to the caller.
+ MBeanServer mbs_null = new MBeanServerWithVersion(null);
+ String version_null = JMX.getSpecificationVersion(mbs_null, null);
+ assertEquals("Version for MBean Server that declares null spec version",
+ null, version_null);
+
+ try {
+ MBeanServer mbs1_2_float = new MBeanServerWithVersion(1.2f);
+ String version1_2_float =
+ JMX.getSpecificationVersion(mbs1_2_float, null);
+ fail("JMX.getSpecificationVersion succeeded with version 1.2f" +
+ " (returned " + version1_2_float + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for non-string version (1.2f)",
+ IOException.class, e.getClass());
+ }
+
+ try {
+ MBeanServer mbs_missing = new MBeanServerWithVersion(POISON_PILL);
+ String version_missing =
+ JMX.getSpecificationVersion(mbs_missing, null);
+ fail("JMX.getSpecificationVersion succeeded with null version" +
+ " (returned " + version_missing + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for missing version",
+ IOException.class, e.getClass());
+ }
+
+ ObjectName wildcardNamespaceName = new ObjectName("foo//*//bar//baz:k=v");
+ try {
+ String brokenVersion =
+ JMX.getSpecificationVersion(mbsc, wildcardNamespaceName);
+ fail("JMX.getSpecificationVersion succeeded with wildcard namespace" +
+ " (returned " + brokenVersion + ")");
+ } catch (Exception e) {
+ assertEquals("Exception for wildcard namespace",
+ IllegalArgumentException.class, e.getClass());
+ }
+
+ String sub1_2namespace = "blibby";
+ JMXNamespace sub1_2 = new JMXNamespace(mbs1_2);
+ ObjectName sub1_2name =
+ JMXNamespaces.getNamespaceObjectName(sub1_2namespace);
+ mbs.registerMBean(sub1_2, sub1_2name);
+ String sub1_2namespaceHandlerVersion =
+ JMX.getSpecificationVersion(mbs, sub1_2name);
+ assertEquals("Spec version of namespace handler",
+ realVersion, sub1_2namespaceHandlerVersion);
+ // The namespace handler is in the top-level namespace so its
+ // version should not be 1.2.
+
+ for (String nameInSub : new String[] {"*:*", "d:k=v"}) {
+ ObjectName subName = new ObjectName(sub1_2namespace + "//" + nameInSub);
+ String subVersion = JMX.getSpecificationVersion(mbs, subName);
+ assertEquals("Spec version in 1.2 namespace (" + nameInSub + ")",
+ "1.2", subVersion);
+ }
+
+ mbs.unregisterMBean(sub1_2name);
+ for (String noSuchNamespace : new String[] {
+ sub1_2namespace + "//*:*", sub1_2namespace + "//d:k=v",
+ }) {
+ try {
+ String brokenVersion = JMX.getSpecificationVersion(
+ mbs, new ObjectName(noSuchNamespace));
+ fail("JMX.getSpecificationVersion succeeded with missing " +
+ "namespace (" + noSuchNamespace + " -> " +
+ brokenVersion);
+ } catch (Exception e) {
+ assertEquals("Exception for missing namespace",
+ IOException.class, e.getClass());
+ }
+ }
+
+ if (failure != null)
+ throw new Exception("TEST FAILED: " + failure);
+ System.out.println("TEST PASSED");
+ }
+
+ private static void assertEquals(String what, Object expect, Object actual) {
+ if (equal(expect, actual))
+ System.out.println("OK: " + what + ": " + expect);
+ else
+ fail(what + ": expected " + expect + ", got " + actual);
+ }
+
+ private static boolean equal(Object x, Object y) {
+ if (x == null)
+ return (y == null);
+ else
+ return x.equals(y);
+ }
+
+ private static void fail(String why) {
+ System.out.println("FAILED: " + why);
+ failure = why;
+ }
+}