diff --git a/src/share/classes/javax/management/AttributeList.java b/src/share/classes/javax/management/AttributeList.java index 1ce3004a337594dfe4c9d5a76f0394bf14a662e7..a629d57ccd4fdaa5c7cc34f759dc0016e51eabeb 100644 --- a/src/share/classes/javax/management/AttributeList.java +++ b/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 { - 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,13 +130,63 @@ public class AttributeList extends ArrayList { // Check for non-Attribute objects // - checkTypeSafe(list); + adding(list); // Build the List // super.addAll(list); } + /** + *

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 {@code + * key} and {@code value} in the {@code Map}, the constructed + * {@code AttributeList} will contain {@link Attribute#Attribute + * Attribute(key, value)}.

+ * + * @param map the {@code Map} defining the elements of the new + * {@code AttributeList}. + */ + public AttributeList(Map map) { + for (Map.Entry entry : map.entrySet()) + add(new Attribute(entry.getKey(), entry.getValue())); + typeSafe = true; + } + + /** + *

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.

+ * + *

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.

+ * + * @return the new {@code Map}. + * + * @throws IllegalArgumentException if this {@code AttributeList} contains + * an element that is not an {@code Attribute}. + */ + public Map toMap() { + Map map = new LinkedHashMap(); + + // 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}. * Changes to the returned value are reflected by changes @@ -154,11 +210,9 @@ public class AttributeList extends ArrayList { */ @SuppressWarnings("unchecked") public List asList() { - if (!typeSafe) { - if (tainted) - checkTypeSafe(this); - typeSafe = true; - } + typeSafe = true; + if (tainted) + adding((Collection) this); // will throw IllegalArgumentException return (List) (List) this; } @@ -175,7 +229,7 @@ public class AttributeList extends ArrayList { * 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 Attribute object to be inserted. @@ -245,8 +299,7 @@ public class AttributeList extends ArrayList { 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 @@ public class AttributeList extends ArrayList { * been called on this instance. */ + /** + * {@inheritDoc} + * @throws IllegalArgumentException if this {@code AttributeList} is + * type-safe 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 + * type-safe 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 + * type-safe 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); } + /** + * {@inheritDoc} + * @throws IllegalArgumentException if this {@code AttributeList} is + * type-safe and {@code c} contains an + * element that is not an {@code Attribute}. + */ @Override public boolean addAll(int index, Collection c) { - if (!tainted) - tainted = isTainted(c); - if (typeSafe) - checkTypeSafe(c); + adding(c); return super.addAll(index, c); } + /** + * {@inheritDoc} + * @throws IllegalArgumentException if this {@code AttributeList} is + * type-safe and {@code element} is not an + * {@code Attribute}. + */ @Override public Object set(int index, Object element) { - if (!tainted) - tainted = isTainted(element); - if (typeSafe) - checkTypeSafe(element); + adding(element); return super.set(index, element); } - /** - * IllegalArgumentException if o is a non-Attribute object. - */ - 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); - } - } - - /** - * Returns true if o is a non-Attribute object. - */ - private static boolean isTainted(Object o) { - try { - checkTypeSafe(o); - } 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; } - /** - * 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(Collection c) { + for (Object x : c) + adding(x); } } diff --git a/src/share/classes/javax/management/JMX.java b/src/share/classes/javax/management/JMX.java index 90770d48110e40479d8a5a90f73047f845c25458..60b9605b3152f891cb2425537415e544b011462a 100644 --- a/src/share/classes/javax/management/JMX.java +++ b/src/share/classes/javax/management/JMX.java @@ -830,4 +830,80 @@ public class JMX { ((DynamicWrapperMBean) mbean).getWrappedObject() : mbean; return (MBeanInjector.injectsSendNotification(resource)); } + + /** + *

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:

+ * + *
+     * String version = JMX.getSpecificationVersion(mbsc, null);
+     * boolean atLeast2dot0 = (version.compareTo("2.0") >= 0);
+     * 
+ * + *

A remote MBean Server might be running an earlier version of the + * JMX API, and in that case certain + * features might not be available in it.

+ * + *

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.

+ * + * @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 Interoperability between + * versions of the JMX specification + * @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); + } + } } diff --git a/src/share/classes/javax/management/MBeanServerConnection.java b/src/share/classes/javax/management/MBeanServerConnection.java index 16ce58bb883a8e3c7cc83d77d73357e1fdfa7b92..fadebc10730bf724a55f4ab745a15c46b24a4e80 100644 --- a/src/share/classes/javax/management/MBeanServerConnection.java +++ b/src/share/classes/javax/management/MBeanServerConnection.java @@ -532,8 +532,30 @@ public interface MBeanServerConnection extends NotificationManager { /** - * Enables the values of several attributes of a named MBean. The MBean - * is identified by its object name. + *

Retrieves the values of several attributes of a named MBean. The MBean + * is identified by its object name.

+ * + *

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.

+ * + *

Here is an example of calling this method and checking that it + * succeeded in retrieving all the requested attributes:

+ * + *
+     * String[] attrNames = ...;
+     * AttributeList list = mbeanServerConnection.getAttributes(objectName, attrNames);
+     * if (list.size() == attrNames.length)
+     *     System.out.println("All attributes were retrieved successfully");
+     * else {
+     *     {@code List} missing = new {@code ArrayList}({@link java.util.Arrays#asList Arrays.asList}(attrNames));
+     *     missing.removeAll(list.toMap().keySet());
+     *     System.out.println("Did not retrieve: " + missing);
+     * }
+     * 
* * @param name The object name of the MBean from which the * attributes are retrieved. @@ -557,6 +579,7 @@ public interface MBeanServerConnection extends NotificationManager { 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 @@ public interface MBeanServerConnection extends NotificationManager { ReflectionException, IOException; - /** - * Sets the values of several attributes of a named MBean. The MBean is - * identified by its object name. + *

Sets the values of several attributes of a named MBean. The MBean is + * identified by its object name.

+ * + *

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.)

+ * + *

Here is an example of calling this method and checking that it + * succeeded in setting all the requested attributes:

+ * + *
+     * AttributeList inputAttrs = ...;
+     * AttributeList outputAttrs = mbeanServerConnection.setAttributes(objectName, inputAttrs);
+     * if (inputAttrs.size() == outputAttrs.size())
+     *     System.out.println("All attributes were set successfully");
+     * else {
+     *     {@code List} missing = new {@code ArrayList}(inputAttrs.toMap().keySet());
+     *     missing.removeAll(outputAttrs.toMap().keySet());
+     *     System.out.println("Did not set: " + missing);
+     * }
+     * 
* * @param name The object name of the MBean within which the * attributes are to be set. @@ -622,7 +671,39 @@ public interface MBeanServerConnection extends NotificationManager { throws InstanceNotFoundException, ReflectionException, IOException; /** - * Invokes an operation on an MBean. + *

Invokes an operation on an MBean.

+ * + *

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:

+ * + *
+     * public interface FooMBean {
+     *     public int countMatches(String[] patterns, boolean ignoreCase);
+     * }
+     * 
+ * + *

The {@code countMatches} operation can be invoked as follows:

+ * + *
+     * String[] myPatterns = ...;
+     * int count = (Integer) mbeanServerConnection.invoke(
+     *         objectName,
+     *         "countMatches",
+     *         new Object[] {myPatterns, true},
+     *         new String[] {String[].class.getName(), boolean.class.getName()});
+     * 
+ * + *

Alternatively, it can be invoked through a proxy as follows:

+ * + *
+     * String[] myPatterns = ...;
+     * FooMBean fooProxy = JMX.newMBeanProxy(
+     *         mbeanServerConnection, objectName, FooMBean.class);
+     * int count = fooProxy.countMatches(myPatterns, true);
+     * 
* * @param name The object name of the MBean on which the method is * to be invoked. @@ -630,7 +711,8 @@ public interface MBeanServerConnection extends NotificationManager { * @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. * diff --git a/src/share/classes/javax/management/MBeanServerNotification.java b/src/share/classes/javax/management/MBeanServerNotification.java index 55e71cec331c3aee18d5e5cc33adb528089babeb..d19c73a555a84342e33450d9c41a36884adf62a9 100644 --- a/src/share/classes/javax/management/MBeanServerNotification.java +++ b/src/share/classes/javax/management/MBeanServerNotification.java @@ -64,6 +64,33 @@ package javax.management; * MBeanServerDelegate.DELEGATE_NAME, printListener, null, null); * * + *

The following code prints a message every time an MBean is registered + * or unregistered in the MBean Server {@code mbeanServer}:

+ * + *
+ * 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);
+ * 
+ * * @since 1.5 */ public class MBeanServerNotification extends Notification { diff --git a/src/share/classes/javax/management/QueryNotificationFilter.java b/src/share/classes/javax/management/QueryNotificationFilter.java index 7d1990fa2b9e1d703f57428aad47d37cd91fdb11..5d7e7815b80af5fd83b4136eb38b26753be272f1 100644 --- a/src/share/classes/javax/management/QueryNotificationFilter.java +++ b/src/share/classes/javax/management/QueryNotificationFilter.java @@ -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 @@ import java.util.Set; * on both the client and the server in the remote case, so using this class * instead is recommended where possible.

* + *

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/src/share/classes/javax/management/openmbean/CompositeDataSupport.java b/src/share/classes/javax/management/openmbean/CompositeDataSupport.java index 4ef93b47611ccc649afec6294b6c993607a171d2..bd7c77af9b4542296b369e52a6bbf6f6a14dae98 100644 --- a/src/share/classes/javax/management/openmbean/CompositeDataSupport.java +++ b/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 { - - // Check compositeType is not null - // - if (compositeType == null) { - throw new IllegalArgumentException("Argument compositeType cannot be null."); - } - - // 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"); + public CompositeDataSupport( + CompositeType compositeType, String[] itemNames, Object[] itemValues) + throws OpenDataException { + this(makeMap(itemNames, itemValues), compositeType); + } - // 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."); - } + private static SortedMap makeMap( + String[] itemNames, Object[] itemValues) + throws OpenDataException { - // 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 +")."); - } - - // 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."); + throw new IllegalArgumentException( + "Different lengths: itemNames[" + itemNames.length + + "], itemValues[" + itemValues.length + "]"); } - // 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 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]); } - // Initialize internal fields: compositeType and contents - // - this.compositeType = compositeType; - 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); + } - - // 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 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"); + + 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 static void checkForNullElement(Object[] arg, String argName) { - if ( (arg == null) || (arg.length == 0) ) { - throw new IllegalArgumentException( - "Argument "+ argName +"[] cannot be null or empty."); + private CompositeDataSupport( + SortedMap items, CompositeType compositeType) + throws OpenDataException { + + // Check compositeType is not null + // + 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:

+ * + *
+     * CompositeData cd1 = ...;
+     * {@code Map} 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);
+     * 
+ * + *

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/src/share/classes/javax/management/package.html b/src/share/classes/javax/management/package.html index 14027d648ea40993dbfd6e59e1f36f4e50941d03..372c30efd9f26216e82a3f6dd2b6b19456c867f6 100644 --- a/src/share/classes/javax/management/package.html +++ b/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:

    -
    +        
         ConfigurationMBean conf =
             {@link javax.management.JMX#newMBeanProxy
                 JMX.newMBeanProxy}(mbs, name, ConfigurationMBean.class);
         int cacheSize = conf.getCacheSize();
         conf.setCacheSize(2000);
         conf.save();
    -      
    +
    -

    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/test/javax/management/MBeanServer/AttributeListMapTest.java b/test/javax/management/MBeanServer/AttributeListMapTest.java new file mode 100644 index 0000000000000000000000000000000000000000..94dd39306a3c64aa9c01dc305cc08f76c0d9d2b1 --- /dev/null +++ b/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/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java b/test/javax/management/MBeanServer/AttributeListTypeSafeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ed2fc96fd06159c10f449e141e6a890b94d0de31 --- /dev/null +++ b/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/test/javax/management/openmbean/CompositeDataToMapTest.java b/test/javax/management/openmbean/CompositeDataToMapTest.java new file mode 100644 index 0000000000000000000000000000000000000000..30641b7c72f4aca073117f8c5fe7b1018a31086e --- /dev/null +++ b/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/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java b/test/javax/management/remote/mandatory/version/JMXSpecVersionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..69782690d0a9eed40108ed7b81de91b5018c5b70 --- /dev/null +++ b/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; + } +}