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
Summary: Small JMX RFEs
Reviewed-by: dfuchs
--- a/jdk/src/share/classes/javax/management/AttributeList.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/AttributeList.java Fri Nov 07 19:19:08 2008 +0100
@@ -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 @@
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 <CODE>AttributeList</CODE> overrides the
- * corresponding methods in the superclass
- * <CODE>ArrayList</CODE>. This is needed in order to insure that the
- * objects contained in the <CODE>AttributeList</CODE> are only
- * <CODE>Attribute</CODE> objects. This avoids getting an exception
- * when retrieving elements from the <CODE>AttributeList</CODE>.
+ * <p>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}.</p>
+ *
+ * <p id="type-safe">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 <em>type-safe</em>, 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.</p>
*
* @since 1.5
*/
@@ -58,8 +64,8 @@
*/
public class AttributeList extends ArrayList<Object> {
- private transient boolean typeSafe;
- private transient boolean tainted;
+ private transient volatile boolean typeSafe;
+ private transient volatile boolean tainted;
/* Serial version */
private static final long serialVersionUID = -4077085769279709076L;
@@ -124,7 +130,7 @@
// Check for non-Attribute objects
//
- checkTypeSafe(list);
+ adding(list);
// Build the List<Attribute>
//
@@ -132,6 +138,56 @@
}
/**
+ * <p>Constructs an {@code AttributeList} containing the elements of
+ * the {@code Map} specified, in the order in which they appear in the
+ * {@code Map}'s {@link Map#entrySet entrySet}. For each <em>{@code
+ * key}</em> and <em>{@code value}</em> in the {@code Map}, the constructed
+ * {@code AttributeList} will contain {@link Attribute#Attribute
+ * Attribute(<em>key</em>, <em>value</em>)}.</p>
+ *
+ * @param map the {@code Map} defining the elements of the new
+ * {@code AttributeList}.
+ */
+ public AttributeList(Map<String, ?> map) {
+ for (Map.Entry<String, ?> entry : map.entrySet())
+ add(new Attribute(entry.getKey(), entry.getValue()));
+ typeSafe = true;
+ }
+
+ /**
+ * <p>Return a {@code Map} that is a snapshot of the values in this
+ * {@code AttributeList}. Each key in the {@code Map} is the {@linkplain
+ * Attribute#getName() name} of an {@code Attribute} in the list, and each
+ * value is the corresponding {@linkplain Attribute#getValue() value} of
+ * that {@code Attribute}. The {@code AttributeList} and the {@code Map}
+ * are unrelated after the call, that is, changes to one do not affect the
+ * other.</p>
+ *
+ * <p>If the {@code AttributeList} contains more than one {@code Attribute}
+ * with the same name, then the {@code Map} will contain an entry
+ * for that name where the value is that of the last of those {@code
+ * Attribute}s.</p>
+ *
+ * @return the new {@code Map}.
+ *
+ * @throws IllegalArgumentException if this {@code AttributeList} contains
+ * an element that is not an {@code Attribute}.
+ */
+ public Map<String, Object> toMap() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+
+ // We can't call adding(this) because we're not necessarily typeSafe
+ if (tainted)
+ throw new IllegalArgumentException("AttributeList contains non-Attribute");
+
+ for (Object x : this) {
+ Attribute a = (Attribute) x;
+ map.put(a.getName(), a.getValue());
+ }
+ return map;
+ }
+
+ /**
* Return a view of this list as a {@code List<Attribute>}.
* Changes to the returned value are reflected by changes
* to the original {@code AttributeList} and vice versa.
@@ -154,11 +210,9 @@
*/
@SuppressWarnings("unchecked")
public List<Attribute> asList() {
- if (!typeSafe) {
- if (tainted)
- checkTypeSafe(this);
- typeSafe = true;
- }
+ typeSafe = true;
+ if (tainted)
+ adding((Collection<?>) this); // will throw IllegalArgumentException
return (List<Attribute>) (List<?>) this;
}
@@ -175,7 +229,7 @@
* Inserts the attribute specified as an element at the position specified.
* Elements with an index greater than or equal to the current position are
* shifted up. If the index is out of range (index < 0 || index >
- * size() a RuntimeOperationsException should be raised, wrapping the
+ * size()) a RuntimeOperationsException should be raised, wrapping the
* java.lang.IndexOutOfBoundsException thrown.
*
* @param object The <CODE>Attribute</CODE> object to be inserted.
@@ -245,8 +299,7 @@
public boolean addAll(int index, AttributeList list) {
try {
return super.addAll(index, list);
- }
- catch (IndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e) {
throw new RuntimeOperationsException(e,
"The specified index is out of range");
}
@@ -258,96 +311,77 @@
* been called on this instance.
*/
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
+ */
@Override
- public boolean add(Object o) {
- if (!tainted)
- tainted = isTainted(o);
- if (typeSafe)
- checkTypeSafe(o);
- return super.add(o);
+ public boolean add(Object element) {
+ adding(element);
+ return super.add(element);
}
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
+ */
@Override
public void add(int index, Object element) {
- if (!tainted)
- tainted = isTainted(element);
- if (typeSafe)
- checkTypeSafe(element);
+ adding(element);
super.add(index, element);
}
+ /**
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code c} contains an
+ * element that is not an {@code Attribute}.
+ */
@Override
public boolean addAll(Collection<?> c) {
- if (!tainted)
- tainted = isTainted(c);
- if (typeSafe)
- checkTypeSafe(c);
+ adding(c);
return super.addAll(c);
}
- @Override
- public boolean addAll(int index, Collection<?> c) {
- if (!tainted)
- tainted = isTainted(c);
- if (typeSafe)
- checkTypeSafe(c);
- return super.addAll(index, c);
- }
-
- @Override
- public Object set(int index, Object element) {
- if (!tainted)
- tainted = isTainted(element);
- if (typeSafe)
- checkTypeSafe(element);
- return super.set(index, element);
- }
-
/**
- * IllegalArgumentException if o is a non-Attribute object.
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code c} contains an
+ * element that is not an {@code Attribute}.
*/
- private static void checkTypeSafe(Object o) {
- try {
- o = (Attribute) o;
- } catch (ClassCastException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- /**
- * IllegalArgumentException if c contains any non-Attribute objects.
- */
- private static void checkTypeSafe(Collection<?> c) {
- try {
- Attribute a;
- for (Object o : c)
- a = (Attribute) o;
- } catch (ClassCastException e) {
- throw new IllegalArgumentException(e);
- }
+ @Override
+ public boolean addAll(int index, Collection<?> c) {
+ adding(c);
+ return super.addAll(index, c);
}
/**
- * Returns true if o is a non-Attribute object.
+ * {@inheritDoc}
+ * @throws IllegalArgumentException if this {@code AttributeList} is
+ * <a href="#type-safe">type-safe</a> and {@code element} is not an
+ * {@code Attribute}.
*/
- private static boolean isTainted(Object o) {
- try {
- checkTypeSafe(o);
- } catch (IllegalArgumentException e) {
- return true;
- }
- return false;
+ @Override
+ public Object set(int index, Object element) {
+ adding(element);
+ return super.set(index, element);
}
- /**
- * Returns true if c contains any non-Attribute objects.
- */
- private static boolean isTainted(Collection<?> c) {
- try {
- checkTypeSafe(c);
- } catch (IllegalArgumentException e) {
- return true;
- }
- return false;
+ private void adding(Object x) {
+ if (x == null || x instanceof Attribute)
+ return;
+ if (typeSafe)
+ throw new IllegalArgumentException("Not an Attribute: " + x);
+ else
+ tainted = true;
+ }
+
+ private void adding(Collection<?> c) {
+ for (Object x : c)
+ adding(x);
}
}
--- a/jdk/src/share/classes/javax/management/JMX.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/JMX.java Fri Nov 07 19:19:08 2008 +0100
@@ -830,4 +830,80 @@
((DynamicWrapperMBean) mbean).getWrappedObject() : mbean;
return (MBeanInjector.injectsSendNotification(resource));
}
+
+ /**
+ * <p>Return the version of the JMX specification that a (possibly remote)
+ * MBean Server is using. The JMX specification described in this
+ * documentation is version 2.0. The earlier versions that might be
+ * reported by this method are 1.0, 1.1, 1.2, and 1.4. (There is no 1.3.)
+ * All of these versions and all future versions can be compared using
+ * {@link String#compareTo(String)}. So, for example, to tell if
+ * {@code mbsc} is running at least version 2.0 you can write:</p>
+ *
+ * <pre>
+ * String version = JMX.getSpecificationVersion(mbsc, null);
+ * boolean atLeast2dot0 = (version.compareTo("2.0") >= 0);
+ * </pre>
+ *
+ * <p>A remote MBean Server might be running an earlier version of the
+ * JMX API, and in that case <a href="package-summary.html#interop">certain
+ * features</a> might not be available in it.</p>
+ *
+ * <p>The version of the MBean Server {@code mbsc} is not necessarily
+ * the version of all namespaces within that MBean Server, for example
+ * if some of them use {@link javax.management.namespace.JMXRemoteNamespace
+ * JMXRemoteNamespace}. To determine the version of the namespace
+ * that a particular MBean is in, give its name as the {@code mbeanName}
+ * parameter.</p>
+ *
+ * @param mbsc a connection to an MBean Server.
+ *
+ * @param mbeanName the name of an MBean within that MBean Server, or null.
+ * If non-null, the namespace of this name, as determined by
+ * {@link JMXNamespaces#getContainingNamespace
+ * JMXNamespaces.getContainingNamespace}, is the one whose specification
+ * version will be returned.
+ *
+ * @return the JMX specification version reported by that MBean Server.
+ *
+ * @throws IllegalArgumentException if {@code mbsc} is null, or if
+ * {@code mbeanName} includes a wildcard character ({@code *} or {@code ?})
+ * in its namespace.
+ *
+ * @throws IOException if the version cannot be obtained, either because
+ * there is a communication problem or because the remote MBean Server
+ * does not have the appropriate {@linkplain
+ * MBeanServerDelegateMBean#getSpecificationVersion() attribute}.
+ *
+ * @see <a href="package-summary.html#interop">Interoperability between
+ * versions of the JMX specification</a>
+ * @see MBeanServerDelegateMBean#getSpecificationVersion
+ */
+ public static String getSpecificationVersion(
+ MBeanServerConnection mbsc, ObjectName mbeanName)
+ throws IOException {
+ if (mbsc == null)
+ throw new IllegalArgumentException("Null MBeanServerConnection");
+
+ String namespace;
+ if (mbeanName == null)
+ namespace = "";
+ else
+ namespace = JMXNamespaces.getContainingNamespace(mbeanName);
+ if (namespace.contains("*") || namespace.contains("?")) {
+ throw new IllegalArgumentException(
+ "ObjectName contains namespace wildcard: " + mbeanName);
+ }
+
+ try {
+ if (namespace.length() > 0)
+ mbsc = JMXNamespaces.narrowToNamespace(mbsc, namespace);
+ return (String) mbsc.getAttribute(
+ MBeanServerDelegate.DELEGATE_NAME, "SpecificationVersion");
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
}
--- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java Fri Nov 07 19:19:08 2008 +0100
@@ -532,8 +532,30 @@
/**
- * Enables the values of several attributes of a named MBean. The MBean
- * is identified by its object name.
+ * <p>Retrieves the values of several attributes of a named MBean. The MBean
+ * is identified by its object name.</p>
+ *
+ * <p>If one or more attributes cannot be retrieved for some reason, they
+ * will be omitted from the returned {@code AttributeList}. The caller
+ * should check that the list is the same size as the {@code attributes}
+ * array. To discover what problem prevented a given attribute from being
+ * retrieved, call {@link #getAttribute getAttribute} for that attribute.</p>
+ *
+ * <p>Here is an example of calling this method and checking that it
+ * succeeded in retrieving all the requested attributes:</p>
+ *
+ * <pre>
+ * String[] attrNames = ...;
+ * AttributeList list = mbeanServerConnection.getAttributes(objectName, attrNames);
+ * if (list.size() == attrNames.length)
+ * System.out.println("All attributes were retrieved successfully");
+ * else {
+ * {@code List<String>} missing = new {@code ArrayList<String>}(<!--
+ * -->{@link java.util.Arrays#asList Arrays.asList}(attrNames));
+ * missing.removeAll(list.toMap().keySet());
+ * System.out.println("Did not retrieve: " + missing);
+ * }
+ * </pre>
*
* @param name The object name of the MBean from which the
* attributes are retrieved.
@@ -557,6 +579,7 @@
throws InstanceNotFoundException, ReflectionException,
IOException;
+
/**
* Sets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name.
@@ -592,10 +615,36 @@
ReflectionException, IOException;
-
/**
- * Sets the values of several attributes of a named MBean. The MBean is
- * identified by its object name.
+ * <p>Sets the values of several attributes of a named MBean. The MBean is
+ * identified by its object name.</p>
+ *
+ * <p>If one or more attributes cannot be set for some reason, they will be
+ * omitted from the returned {@code AttributeList}. The caller should check
+ * that the input {@code AttributeList} is the same size as the output one.
+ * To discover what problem prevented a given attribute from being retrieved,
+ * it will usually be possible to call {@link #setAttribute setAttribute}
+ * for that attribute, although this is not guaranteed to work. (For
+ * example, the values of two attributes may have been rejected because
+ * they were inconsistent with each other. Setting one of them alone might
+ * be allowed.)<p>
+ *
+ * <p>Here is an example of calling this method and checking that it
+ * succeeded in setting all the requested attributes:</p>
+ *
+ * <pre>
+ * AttributeList inputAttrs = ...;
+ * AttributeList outputAttrs = mbeanServerConnection.setAttributes(<!--
+ * -->objectName, inputAttrs);
+ * if (inputAttrs.size() == outputAttrs.size())
+ * System.out.println("All attributes were set successfully");
+ * else {
+ * {@code List<String>} missing = new {@code ArrayList<String>}(<!--
+ * -->inputAttrs.toMap().keySet());
+ * missing.removeAll(outputAttrs.toMap().keySet());
+ * System.out.println("Did not set: " + missing);
+ * }
+ * </pre>
*
* @param name The object name of the MBean within which the
* attributes are to be set.
@@ -622,7 +671,39 @@
throws InstanceNotFoundException, ReflectionException, IOException;
/**
- * Invokes an operation on an MBean.
+ * <p>Invokes an operation on an MBean.</p>
+ *
+ * <p>Because of the need for a {@code signature} to differentiate
+ * possibly-overloaded operations, it is much simpler to invoke operations
+ * through an {@linkplain JMX#newMBeanProxy(MBeanServerConnection, ObjectName,
+ * Class) MBean proxy} where possible. For example, suppose you have a
+ * Standard MBean interface like this:</p>
+ *
+ * <pre>
+ * public interface FooMBean {
+ * public int countMatches(String[] patterns, boolean ignoreCase);
+ * }
+ * </pre>
+ *
+ * <p>The {@code countMatches} operation can be invoked as follows:</p>
+ *
+ * <pre>
+ * String[] myPatterns = ...;
+ * int count = (Integer) mbeanServerConnection.invoke(
+ * objectName,
+ * "countMatches",
+ * new Object[] {myPatterns, true},
+ * new String[] {String[].class.getName(), boolean.class.getName()});
+ * </pre>
+ *
+ * <p>Alternatively, it can be invoked through a proxy as follows:</p>
+ *
+ * <pre>
+ * String[] myPatterns = ...;
+ * FooMBean fooProxy = JMX.newMBeanProxy(
+ * mbeanServerConnection, objectName, FooMBean.class);
+ * int count = fooProxy.countMatches(myPatterns, true);
+ * </pre>
*
* @param name The object name of the MBean on which the method is
* to be invoked.
@@ -630,7 +711,8 @@
* @param params An array containing the parameters to be set when
* the operation is invoked
* @param signature An array containing the signature of the
- * operation. The class objects will be loaded using the same
+ * operation, an array of class names in the format returned by
+ * {@link Class#getName()}. The class objects will be loaded using the same
* class loader as the one used for loading the MBean on which the
* operation was invoked.
*
--- a/jdk/src/share/classes/javax/management/MBeanServerNotification.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/MBeanServerNotification.java Fri Nov 07 19:19:08 2008 +0100
@@ -64,6 +64,33 @@
* MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
* </pre>
*
+ * <p>The following code prints a message every time an MBean is registered
+ * or unregistered in the MBean Server {@code mbeanServer}:</p>
+ *
+ * <pre>
+ * private static final NotificationListener printListener = new NotificationListener() {
+ * public void handleNotification(Notification n, Object handback) {
+ * if (!(n instanceof MBeanServerNotification)) {
+ * System.out.println("Ignored notification of class " + n.getClass().getName());
+ * return;
+ * }
+ * MBeanServerNotification mbsn = (MBeanServerNotification) n;
+ * String what;
+ * if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
+ * what = "MBean registered";
+ * else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
+ * what = "MBean unregistered";
+ * else
+ * what = "Unknown type " + n.getType();
+ * System.out.println("Received MBean Server notification: " + what + ": " +
+ * mbsn.getMBeanName());
+ * };
+ *
+ * ...
+ * mbeanServer.addNotificationListener(
+ * MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
+ * </pre>
+ *
* @since 1.5
*/
public class MBeanServerNotification extends Notification {
--- a/jdk/src/share/classes/javax/management/QueryNotificationFilter.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/QueryNotificationFilter.java Fri Nov 07 19:19:08 2008 +0100
@@ -26,7 +26,6 @@
package javax.management;
import com.sun.jmx.mbeanserver.NotificationMBeanSupport;
-import com.sun.jmx.mbeanserver.Util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -43,6 +42,11 @@
* on both the client and the server in the remote case, so using this class
* instead is recommended where possible.</p>
*
+ * <p>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.</p>
+ *
* <p>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"},
--- a/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Fri Nov 07 19:19:08 2008 +0100
@@ -33,12 +33,14 @@
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 @@
* respective values.
* A {@link SortedMap} is used for faster retrieval of elements.
*/
- private SortedMap<String, Object> contents = new TreeMap<String, Object>();
+ private final SortedMap<String, Object> contents;
/**
* @serial The <i>composite type </i> of this <i>composite data</i> instance.
*/
- private CompositeType compositeType;
+ private final CompositeType compositeType;
/**
- * <p>
- * Constructs a <tt>CompositeDataSupport</tt> instance with the specified
+ * <p>Constructs a <tt>CompositeDataSupport</tt> instance with the specified
* <tt>compositeType</tt>, whose item values
* are specified by <tt>itemValues[]</tt>, in the same order as in
* <tt>itemNames[]</tt>.
@@ -79,41 +80,124 @@
* The items contained in this <tt>CompositeDataSupport</tt> instance are
* internally stored in a <tt>TreeMap</tt>,
* thus sorted in ascending lexicographic order of their names, for faster
- * retrieval of individual item values.
- * <p>
- * The constructor checks that all the constraints listed below for each
+ * retrieval of individual item values.</p>
+ *
+ * <p>The constructor checks that all the constraints listed below for each
* parameter are satisfied,
- * and throws the appropriate exception if they are not.
- * <p>
- * @param compositeType the <i>composite type </i> of this <i>composite
- * data</i> instance;
- * must not be null.
- * <p>
- * @param itemNames <tt>itemNames</tt> must list, in any order, all the
- * item names defined in <tt>compositeType</tt>;
- * the order in which the names are listed, is used to
- * match values in <tt>itemValues[]</tt>;
- * must not be null or empty.
+ * and throws the appropriate exception if they are not.</p>
+ *
+ * @param compositeType the <i>composite type </i> of this <i>composite
+ * data</i> instance; must not be null.
+ *
+ * @param itemNames <tt>itemNames</tt> must list, in any order, all the
+ * item names defined in <tt>compositeType</tt>; the order in which the
+ * names are listed, is used to match values in <tt>itemValues[]</tt>; must
+ * not be null.
+ *
+ * @param itemValues the values of the items, listed in the same order as
+ * their respective names in <tt>itemNames</tt>; each item value can be
+ * null, but if it is non-null it must be a valid value for the open type
+ * defined in <tt>compositeType</tt> for the corresponding item; must be of
+ * the same size as <tt>itemNames</tt>; must not be null.
+ *
+ * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
+ * <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty, or one
+ * of the elements in <tt>itemNames[]</tt> is a null or empty string, or
+ * <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size.
+ *
+ * @throws OpenDataException <tt>itemNames[]</tt> or
+ * <tt>itemValues[]</tt>'s size differs from the number of items defined in
+ * <tt>compositeType</tt>, or one of the elements in <tt>itemNames[]</tt>
+ * does not exist as an item name defined in <tt>compositeType</tt>, or one
+ * of the elements in <tt>itemValues[]</tt> is not a valid value for the
+ * corresponding item as defined in <tt>compositeType</tt>.
+ */
+ public CompositeDataSupport(
+ CompositeType compositeType, String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
+ this(makeMap(itemNames, itemValues), compositeType);
+ }
+
+ private static SortedMap<String, Object> makeMap(
+ String[] itemNames, Object[] itemValues)
+ throws OpenDataException {
+
+ if (itemNames == null || itemValues == null)
+ throw new IllegalArgumentException("Null itemNames or itemValues");
+ if (itemNames.length != itemValues.length) {
+ throw new IllegalArgumentException(
+ "Different lengths: itemNames[" + itemNames.length +
+ "], itemValues[" + itemValues.length + "]");
+ }
+
+ SortedMap<String, Object> map = new TreeMap<String, Object>();
+ 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]);
+ }
+
+ return map;
+ }
+
+ /**
* <p>
- * @param itemValues the values of the items, listed in the same order as
- * their respective names in <tt>itemNames</tt>;
- * each item value can be null, but if it is non-null it must be
- * a valid value for the open type defined in <tt>compositeType</tt> for the corresponding item;
- * must be of the same size as <tt>itemNames</tt>; must not be null or empty.
- * <p>
- * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>itemNames[]</tt> or <tt>itemValues[]</tt> is null or empty,
- * or one of the elements in <tt>itemNames[]</tt> is a null or empty string,
- * or <tt>itemNames[]</tt> and <tt>itemValues[]</tt> are not of the same size.
- * <p>
- * @throws OpenDataException <tt>itemNames[]</tt> or <tt>itemValues[]</tt>'s size differs from
- * the number of items defined in <tt>compositeType</tt>,
- * or one of the elements in <tt>itemNames[]</tt> does not exist as an item name defined in <tt>compositeType</tt>,
- * or one of the elements in <tt>itemValues[]</tt> is not a valid value for the corresponding item
- * as defined in <tt>compositeType</tt>.
- * <p>
+ * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item names and corresponding values
+ * are given by the mappings in the map <tt>items</tt>.
+ * This constructor converts the keys to a string array and the values to an object array and calls
+ * <tt>CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[])</tt>.
+ *
+ * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
+ * must not be null.
+ * @param items the mappings of all the item names to their values;
+ * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>;
+ * must not be null.
+ *
+ * @throws IllegalArgumentException <tt>compositeType</tt> is null, or
+ * <tt>items</tt> is null, or one of the keys in <tt>items</tt> is a null
+ * or empty string.
+ * @throws OpenDataException <tt>items</tt>' size differs from the
+ * number of items defined in <tt>compositeType</tt>, or one of the
+ * keys in <tt>items</tt> does not exist as an item name defined in
+ * <tt>compositeType</tt>, or one of the values in <tt>items</tt>
+ * is not a valid value for the corresponding item as defined in
+ * <tt>compositeType</tt>.
+ * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of
+ * the class <tt>java.lang.String</tt>.
+ *
+ * @see #toMap
*/
- public CompositeDataSupport(CompositeType compositeType, String[] itemNames, Object[] itemValues)
- throws OpenDataException {
+ public CompositeDataSupport(CompositeType compositeType,
+ Map<String,?> items)
+ throws OpenDataException {
+ this(makeMap(items), compositeType);
+ }
+
+ private static SortedMap<String, Object> makeMap(Map<String, ?> items) {
+ if (items == null)
+ throw new IllegalArgumentException("Null items map");
+ if (items.containsKey(null) || items.containsKey(""))
+ throw new IllegalArgumentException("Null or empty item name");
+
+ SortedMap<String, Object> map = new TreeMap<String, Object>();
+ 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<String, Object> items, CompositeType compositeType)
+ throws OpenDataException {
// Check compositeType is not null
//
@@ -122,126 +206,42 @@
}
// item names defined in compositeType:
- Set<String> 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");
+ Set<String> namesFromType = compositeType.keySet();
+ Set<String> namesFromItems = items.keySet();
- // 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.length != itemValues.length) {
- throw new IllegalArgumentException("Array arguments itemNames[] and itemValues[] "+
- "should be of same length (got "+ itemNames.length +
- " and "+ itemValues.length +").");
+ // This is just a comparison, but we do it this way for a better
+ // exception message.
+ if (!namesFromType.equals(namesFromItems)) {
+ Set<String> extraFromType = new TreeSet<String>(namesFromType);
+ extraFromType.removeAll(namesFromItems);
+ Set<String> extraFromItems = new TreeSet<String>(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);
+ }
}
- // 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.");
- }
-
- // 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; i<itemValues.length; i++) {
- itemType = compositeType.getType(itemNames[i]);
- if ( (itemValues[i] != null) && (! itemType.isValue(itemValues[i])) ) {
- throw new OpenDataException("Argument's element itemValues["+ i +"]=\""+ itemValues[i] +"\" is not a valid value for"+
- " this item (itemName="+ itemNames[i] +",itemType="+ itemType +").");
+ // Check each value, if not null, is of the open type defined for the
+ // corresponding item
+ for (String name : namesFromType) {
+ Object value = items.get(name);
+ if (value != null) {
+ OpenType<?> 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;
- for (int i=0; i<itemNames.length; i++) {
- this.contents.put(itemNames[i], itemValues[i]);
- }
- }
-
- /**
- * <p>
- * Constructs a <tt>CompositeDataSupport</tt> instance with the specified <tt>compositeType</tt>, whose item names and corresponding values
- * are given by the mappings in the map <tt>items</tt>.
- * This constructor converts the keys to a string array and the values to an object array and calls
- * <tt>CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[])</tt>.
- * <p>
- * @param compositeType the <i>composite type </i> of this <i>composite data</i> instance;
- * must not be null.
- * <p>
- * @param items the mappings of all the item names to their values;
- * <tt>items</tt> must contain all the item names defined in <tt>compositeType</tt>;
- * must not be null or empty.
- * <p>
- * @throws IllegalArgumentException <tt>compositeType</tt> is null, or <tt>items</tt> is null or empty,
- * or one of the keys in <tt>items</tt> is a null or empty string,
- * or one of the values in <tt>items</tt> is null.
- * <p>
- * @throws OpenDataException <tt>items</tt>' size differs from the number of items defined in <tt>compositeType</tt>,
- * or one of the keys in <tt>items</tt> does not exist as an item name defined in <tt>compositeType</tt>,
- * or one of the values in <tt>items</tt> is not a valid value for the corresponding item
- * as defined in <tt>compositeType</tt>.
- * <p>
- * @throws ArrayStoreException one or more keys in <tt>items</tt> is not of the class <tt>java.lang.String</tt>.
- * <p>
- */
- public CompositeDataSupport(CompositeType compositeType,
- Map<String,?> items)
- throws OpenDataException {
-
-
- // Let the other constructor do the job, as the call to another constructor must be the first call
- //
- 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.");
- }
- for (int i=0; i<arg.length; i++) {
- if (arg[i] == null) {
- throw new IllegalArgumentException(
- "Argument's element "+ argName +"["+ i +"] cannot be null.");
- }
- }
- }
-
- /**
- *
- */
- private static void checkForEmptyString(String[] arg, String argName) {
- for (int i=0; i<arg.length; i++) {
- if (arg[i].trim().equals("")) {
- throw new IllegalArgumentException(
- "Argument's element "+ argName +"["+ i +"] cannot be an empty string.");
- }
- }
+ this.contents = items;
}
/**
@@ -329,6 +329,54 @@
}
/**
+ * <p>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.</p>
+ *
+ * <p>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:</p>
+ *
+ * <pre>
+ * CompositeData cd1 = ...;
+ * {@code Map<String, Object>} map = CompositeDataSupport.toMap(cd1);
+ * assert(map.get("id") instanceof Integer);
+ * map.put("id", 253);
+ * CompositeData cd2 = {@link #CompositeDataSupport(CompositeType, Map)
+ * new CompositeDataSupport}(cd1.getCompositeType(), map);
+ * </pre>
+ *
+ * <p>Logically, this method would be a method in the {@link CompositeData}
+ * interface, but cannot be for compatibility reasons.</p>
+ *
+ * @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<String, Object> 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<String, Object> map = new LinkedHashMap<String, Object>();
+ CompositeType ct = cd.getCompositeType();
+ for (String key : ct.keySet()) {
+ Object value = cd.get(key);
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ /**
* Compares the specified <var>obj</var> parameter with this
* <code>CompositeDataSupport</code> instance for equality.
* <p>
--- a/jdk/src/share/classes/javax/management/package.html Fri Nov 07 11:48:07 2008 +0100
+++ b/jdk/src/share/classes/javax/management/package.html Fri Nov 07 19:19:08 2008 +0100
@@ -1,7 +1,7 @@
<html>
-<head>
-<title>javax.management package</title>
-<!--
+ <head>
+ <title>javax.management package</title>
+ <!--
Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -24,37 +24,37 @@
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or
have any questions.
--->
-</head>
-<body bgcolor="white">
- <p>Provides the core classes for the Java Management Extensions.</p>
+ -->
+ </head>
+ <body bgcolor="white">
+ <p>Provides the core classes for the Java Management Extensions.</p>
- <p>The Java Management Extensions
- (JMX<sup><font size="-1">TM</font></sup>) API is a standard
- API for management and monitoring. Typical uses include:</p>
+ <p>The Java Management Extensions
+ (JMX<sup><font size="-1">TM</font></sup>) API is a standard
+ API for management and monitoring. Typical uses include:</p>
- <ul>
- <li>consulting and changing application configuration</li>
+ <ul>
+ <li>consulting and changing application configuration</li>
- <li>accumulating statistics about application behavior and
- making them available</li>
+ <li>accumulating statistics about application behavior and
+ making them available</li>
- <li>notifying of state changes and erroneous conditions.</li>
- </ul>
+ <li>notifying of state changes and erroneous conditions.</li>
+ </ul>
- <p>The JMX API can also be used as part of a solution for
- managing systems, networks, and so on.</p>
+ <p>The JMX API can also be used as part of a solution for
+ managing systems, networks, and so on.</p>
- <p>The API includes remote access, so a remote management
- program can interact with a running application for these
- purposes.</p>
+ <p>The API includes remote access, so a remote management
+ program can interact with a running application for these
+ purposes.</p>
- <h2>MBeans</h2>
+ <h2>MBeans</h2>
- <p>The fundamental notion of the JMX API is the <em>MBean</em>.
- An MBean is a named <em>managed object</em> representing a
- resource. It has a <em>management interface</em> consisting
- of:</p>
+ <p>The fundamental notion of the JMX API is the <em>MBean</em>.
+ An MBean is a named <em>managed object</em> representing a
+ resource. It has a <em>management interface</em> consisting
+ of:</p>
<ul>
<li>named and typed attributes that can be read and/or
@@ -92,40 +92,40 @@
<pre>
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();
}
- </pre>
+ </pre>
- <p>The methods <code>getCacheSize</code> and
- <code>setCacheSize</code> define a read-write attribute of
- type <code>int</code> called <code>CacheSize</code> (with an
- initial capital, unlike the JavaBeans convention).</p>
+ <p>The methods <code>getCacheSize</code> and
+ <code>setCacheSize</code> define a read-write attribute of
+ type <code>int</code> called <code>CacheSize</code> (with an
+ initial capital, unlike the JavaBeans convention).</p>
- <p>The method <code>getLastChangedTime</code> defines an
- attribute of type <code>long</code> called
- <code>LastChangedTime</code>. This is a read-only attribute,
- since there is no method <code>setLastChangedTime</code>.</p>
+ <p>The method <code>getLastChangedTime</code> defines an
+ attribute of type <code>long</code> called
+ <code>LastChangedTime</code>. This is a read-only attribute,
+ since there is no method <code>setLastChangedTime</code>.</p>
- <p>The method <code>save</code> defines an operation called
- <code>save</code>. It is not an attribute, since its name
- does not begin with <code>get</code>, <code>set</code>, or
- <code>is</code>.</p>
+ <p>The method <code>save</code> defines an operation called
+ <code>save</code>. It is not an attribute, since its name
+ does not begin with <code>get</code>, <code>set</code>, or
+ <code>is</code>.</p>
- <p>The exact naming patterns for Standard MBeans are detailed in
- the <a href="#spec">JMX Specification</a>.</p>
+ <p>The exact naming patterns for Standard MBeans are detailed in
+ the <a href="#spec">JMX Specification</a>.</p>
- <p>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 <code>MBean</code> suffix. So in
- the example the object would be of the class
- <code>Configuration</code>, in the same Java package as
- <code>ConfigurationMBean</code>. The second way is to use the
- {@link javax.management.StandardMBean StandardMBean}
- class.</p>
+ <p>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 <code>MBean</code> suffix. So in
+ the example the object would be of the class
+ <code>Configuration</code>, in the same Java package as
+ <code>ConfigurationMBean</code>. The second way is to use the
+ {@link javax.management.StandardMBean StandardMBean}
+ class.</p>
<h3 id="stdannot">Defining Standard MBeans with annotations</h3>
@@ -272,37 +272,37 @@
<pre>
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]);
- </pre>
+ </pre>
<p id="proxy">Alternatively, if you have a Java interface that
corresponds to the management interface for the MBean, you can use an
<em>MBean proxy</em> like this:</p>
- <pre>
+ <pre>
ConfigurationMBean conf =
{@link javax.management.JMX#newMBeanProxy
JMX.newMBeanProxy}(mbs, name, ConfigurationMBean.class);
int cacheSize = conf.getCacheSize();
conf.setCacheSize(2000);
conf.save();
- </pre>
+ </pre>
- <p>Using an MBean proxy is just a convenience. The second
- example ends up calling the same <code>MBeanServer</code>
- operations as the first one.</p>
+ <p>Using an MBean proxy is just a convenience. The second
+ example ends up calling the same <code>MBeanServer</code>
+ operations as the first one.</p>
- <p>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.</p>
+ <p>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.</p>
<h3>MBean lifecycle and resource injection</h3>
@@ -407,6 +407,92 @@
So for example an SNMP GET operation might result in a
<code>getAttribute</code> on the MBean Server.</p>
+ <h3 id="interop">Interoperability between versions of the JMX
+ specification</h3>
+
+ <p>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.</p>
+
+ <p>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}.</p>
+
+ <p>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.</p>
+
+ <h4 id="interop-1.4">If the remote MBean Server is 1.4</h4>
+
+ <ul>
+
+ <li><p>You cannot use {@link
+ javax.management.QueryNotificationFilter
+ QueryNotificationFilter} in {@link
+ javax.management.MBeanServerConnection#addNotificationListener
+ addNotificationListener} since this class did not exist
+ in 1.4.</p>
+
+ <li><p>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")}.</p>
+
+ <li><p>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.</p>
+
+ <li><p>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.</p>
+ </ul>
+
+ <h4 id="interop-1.2">If the remote MBean Server is 1.2</h4>
+
+ <p><b>In addition to the above</b>,</p>
+
+ <ul>
+
+ <li><p>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,*}.</p>
+
+ <li><p>You cannot use {@link
+ javax.management.Query#isInstanceOf Query.isInstanceOf}
+ in a query.</p>
+
+ <li><p>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.</p>
+
+ </ul>
+
<p id="spec">
@see <a href="{@docRoot}/../technotes/guides/jmx/index.html">
Java SE 6 Platform documentation on JMX technology</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/AttributeListMapTest.java Fri Nov 07 19:19:08 2008 +0100
@@ -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<String, Object> map = attrs.toMap();
+ final Map<String, Object> expectMap = new HashMap<String, Object>();
+ 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<Object>(expectMap.values()),
+ new HashSet<Object>(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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java Fri Nov 07 19:19:08 2008 +0100
@@ -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<Attribute> 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<Attribute> 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;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/openmbean/CompositeDataToMapTest.java Fri Nov 07 19:19:08 2008 +0100
@@ -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> T wrap(T x, Class<T> 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.<String, Object>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<String, Object> namedNumberMap = new HashMap<String, Object>();
+ 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";
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java Fri Nov 07 19:19:08 2008 +0100
@@ -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<Attribute> 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<ObjectName> 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<ObjectName> 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;
+ }
+}