From 3123299828eb739e5a4d87418d1cd81ef780094f Mon Sep 17 00:00:00 2001
From: Daniel Fuchs The following code prints a message every time an MBean is registered
- * or unregistered in the MBean Server {@code mbeanServer}:
+ * An MBean which is not an {@link MBeanServerDelegate} may also emit
+ * MBeanServerNotifications. In particular, a custom subclass of the
+ * {@link javax.management.namespace.JMXDomain JMXDomain} MBean or a custom
+ * subclass of the {@link javax.management.namespace.JMXNamespace JMXNamespace}
+ * MBean may emit an MBeanServerNotification for a group of MBeans.
+ * An MBeanServerNotification emitted to denote the registration or
+ * unregistration of a group of MBeans has the following characteristics:
+ *
+ *
+ * MBeans which emit these group registration/unregistration notifications will + * declare them in their {@link MBeanInfo#getNotifications() + * MBeanNotificationInfo}. + *
+ *+ * To receive a group MBeanServerNotification, you need to register a listener + * with the MBean that emits it. For instance, assuming that the {@link + * javax.management.namespace.JMXNamespace JMXNamespace} MBean handling + * namespace {@code "foo"} has declared that it emits such a notification, + * you will need to register your notification listener with that MBean, which + * will be named {@link + * javax.management.namespace.JMXNamespaces#getNamespaceObjectName(java.lang.String) + * foo//:type=JMXNamespace}. + *
+ *The following code prints a message every time a group of MBean is + * registered or unregistered in the namespace {@code "foo"}, assumimg its + * {@link javax.management.namespace.JMXNamespace handler} supports + * group MBeanServerNotifications:
* *
* private static final NotificationListener printListener = new NotificationListener() {
@@ -76,19 +116,33 @@ package javax.management;
* }
* MBeanServerNotification mbsn = (MBeanServerNotification) n;
* String what;
- * if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION))
+ * ObjectName[] names = null;
+ * if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
* what = "MBean registered";
- * else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION))
+ * } else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
* what = "MBean unregistered";
- * else
+ * } else if (n.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION+".group")) {
+ * what = "Group of MBeans registered matching";
+ * if (mbsn.getUserData() instanceof ObjectName[])
+ * names = (ObjectName[]) mbsn.getUserData();
+ * } else if (n.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION+".group")) {
+ * what = "Group of MBeans unregistered matching";
+ * if (mbsn.getUserData() instanceof ObjectName[])
+ * names = (ObjectName[]) mbsn.getUserData();
+ * } else
* what = "Unknown type " + n.getType();
* System.out.println("Received MBean Server notification: " + what + ": " +
* mbsn.getMBeanName());
+ * if (names != null) {
+ * for (ObjectName mb : names)
+ * System.out.println("\t"+mb);
+ * }
+ * }
* };
*
* ...
* mbeanServer.addNotificationListener(
- * MBeanServerDelegate.DELEGATE_NAME, printListener, null, null);
+ * JMXNamespaces.getNamespaceObjectName("foo"), printListener, null, null);
*
*
* @since 1.5
From 606d3d6cd104a25c9902e96b892bed5ba120a38d Mon Sep 17 00:00:00 2001
From: Xiomara Jayasena
@@ -52,7 +58,11 @@ import java.lang.annotation.Target;
* {@link MBean @MBean}
* {@code @NotificationInfo}(types={"com.example.notifs.create",
* "com.example.notifs.destroy"})
- * public class Cache {...}
+ * public class Cache {
+ * {@code @Resource}
+ * private volatile SendNotification sendNotification;
+ * ...
+ * }
*
*
* Each {@code @NotificationInfo} produces an {@link @@ -64,6 +74,13 @@ import java.lang.annotation.Target; * several {@code @NotificationInfo} annotations into a containing * {@link NotificationInfos @NotificationInfos} annotation. * + *
The {@code @NotificationInfo} and {@code @NotificationInfos} annotations + * are ignored on an MBean that is not a {@linkplain JMX#isNotificationSource + * notification source} or that implements {@link NotificationBroadcaster} and + * returns a non-empty array from its {@link + * NotificationBroadcaster#getNotificationInfo() getNotificationInfo()} + * method.
+ * *The {@code NotificationInfo} and {@code NotificationInfos} * annotations can be applied to the MBean implementation class, or to * any parent class or interface. These annotations on a class take @@ -71,7 +88,8 @@ import java.lang.annotation.Target; * If an MBean does not have these annotations on its class or any * superclass, then superinterfaces are examined. It is an error for * more than one superinterface to have these annotations, unless one - * of them is a child of all the others.
+ * of them is a descendant of all the others; registering such an erroneous + * MBean will cause a {@link NotCompliantMBeanException}. */ @Documented @Inherited diff --git a/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java b/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java index a995ee59b60..aa9b34b3cdb 100644 --- a/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java +++ b/jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java @@ -38,19 +38,34 @@ import javax.management.AttributeChangeNotification; import javax.management.Description; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; +import javax.management.ListenerNotFoundException; import javax.management.MBean; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanServer; import javax.management.MXBean; +import javax.management.Notification; +import javax.management.NotificationBroadcaster; import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; import javax.management.NotificationInfo; import javax.management.NotificationInfos; +import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.SendNotification; public class AnnotatedNotificationInfoTest { - // Data for the first test. This tests that MBeanNotificationInfo + + static final Descriptor expectedDescriptor = new ImmutableDescriptor( + "foo=bar", "descriptionResourceBundleBaseName=bundle", + "descriptionResourceKey=key"); + static final MBeanNotificationInfo expected = new MBeanNotificationInfo( + new String[] {"foo", "bar"}, + AttributeChangeNotification.class.getName(), + "description", + expectedDescriptor); + + // Data for the first kind of test. This tests that MBeanNotificationInfo // is correctly derived from @NotificationInfo. // Every static field called mbean* is expected to be an MBean // with a single MBeanNotificationInfo that has the same value @@ -254,11 +269,48 @@ public class AnnotatedNotificationInfoTest { private static Object mbeanMXBean2 = new MXBean2(); - // Classes for the second test. This tests the simplest case, which is - // the first example in the javadoc for @NotificationInfo. Notice that - // this MBean is not a NotificationBroadcaster and does not inject a - // SendNotification! That should possibly be an error, but it's currently - // allowed by the spec. + // Test that @NotificationInfo and @NotificationInfos are ignored if + // the MBean returns a non-empty MBeanNotificationInfo[] from its + // NotificationBroadcaster.getNotifications() implementation. + + @NotificationInfo(types={"blim", "blam"}) + public static interface Explicit1MBean {} + + public static class Explicit1 + extends NotificationBroadcasterSupport implements Explicit1MBean { + public Explicit1() { + super(expected); + } + } + + private static Object mbeanExplicit1 = new Explicit1(); + + @NotificationInfos( + { + @NotificationInfo(types="blim"), @NotificationInfo(types="blam") + } + ) + public static interface Explicit2MXBean {} + + public static class Explicit2 + implements NotificationBroadcaster, Explicit2MXBean { + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) {} + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException {} + + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] {expected}; + } + } + + // Data for the second kind of test. This tests that @NotificationInfo is + // ignored if the MBean is not a notification source. Every static + // field called ignoredMBean* is expected to be an MBean on which + // isInstanceOf(NotificationBroadcaster.class.getName() is false, + // addNotificationListener produces an exception, and the + // MBeanNotificationInfo array is empty. @NotificationInfo(types={"com.example.notifs.create", "com.example.notifs.destroy"}) public static interface CacheMBean { @@ -271,6 +323,73 @@ public class AnnotatedNotificationInfoTest { } } + private static Object ignoredMBean1 = new Cache(); + + @NotificationInfos( + @NotificationInfo(types={"foo", "bar"}) + ) + public static interface Cache2MBean { + public int getCachedNum(); + } + + public static class Cache2 implements Cache2MBean { + public int getCachedNum() { + return 0; + } + } + + private static Object ignoredMBean2 = new Cache2(); + + private static final NotificationListener nullListener = + new NotificationListener() { + public void handleNotification( + Notification notification, Object handback) {} + }; + + // Test that inheriting inconsistent @NotificationInfo annotations is + // an error, but not if they are overridden by a non-empty getNotifications() + + @NotificationInfo(types={"blim"}) + public static interface Inconsistent1 {} + + @NotificationInfo(types={"blam"}) + public static interface Inconsistent2 {} + + public static interface InconsistentMBean extends Inconsistent1, Inconsistent2 {} + + public static class Inconsistent + extends NotificationBroadcasterSupport implements InconsistentMBean {} + + public static class Consistent + extends Inconsistent implements NotificationBroadcaster { + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) {} + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException {} + + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] {expected}; + } + } + + private static Object mbeanConsistent = new Consistent(); + + @NotificationInfo( + types = {"foo", "bar"}, + notificationClass = AttributeChangeNotification.class, + description = @Description( + value = "description", + bundleBaseName = "bundle", + key = "key"), + descriptorFields = {"foo=bar"}) + public static interface Consistent2MBean extends Inconsistent1, Inconsistent2 {} + + public static class Consistent2 + extends NotificationBroadcasterSupport implements Consistent2MBean {} + + private static Object mbeanConsistent2 = new Consistent2(); + public static void main(String[] args) throws Exception { if (!AnnotatedNotificationInfoTest.class.desiredAssertionStatus()) throw new Exception("Test must be run with -ea"); @@ -278,37 +397,46 @@ public class AnnotatedNotificationInfoTest { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName on = new ObjectName("a:b=c"); - Descriptor expectedDescriptor = new ImmutableDescriptor( - "foo=bar", "descriptionResourceBundleBaseName=bundle", - "descriptionResourceKey=key"); - MBeanNotificationInfo expected = new MBeanNotificationInfo( - new String[] {"foo", "bar"}, - AttributeChangeNotification.class.getName(), - "description", - expectedDescriptor); - System.out.println("Testing MBeans..."); for (Field mbeanField : AnnotatedNotificationInfoTest.class.getDeclaredFields()) { - if (!mbeanField.getName().startsWith("mbean")) + boolean notifier; + if (mbeanField.getName().startsWith("mbean")) + notifier = true; + else if (mbeanField.getName().startsWith("ignoredMBean")) + notifier = false; + else continue; System.out.println("..." + mbeanField.getName()); Object mbean = mbeanField.get(null); mbs.registerMBean(mbean, on); MBeanInfo mbi = mbs.getMBeanInfo(on); MBeanNotificationInfo[] mbnis = mbi.getNotifications(); - assert mbnis.length == 1 : mbnis.length; - assert mbnis[0].equals(expected) : mbnis[0]; + if (notifier) { + assert mbnis.length == 1 : mbnis.length; + assert mbnis[0].equals(expected) : mbnis[0]; + } else { + assert mbnis.length == 0 : mbnis.length; + assert !mbs.isInstanceOf(on, NotificationBroadcaster.class.getName()); + try { + mbs.addNotificationListener(on, nullListener, null, null); + assert false : "addNotificationListener works"; + } catch (Exception e) { + // OK: addNL correctly refused + } + } mbs.unregisterMBean(on); } - mbs.registerMBean(new Cache(), on); - MBeanInfo mbi = mbs.getMBeanInfo(on); - MBeanNotificationInfo[] mbnis = mbi.getNotifications(); - assert mbnis.length == 1 : mbnis.length; - String[] types = mbnis[0].getNotifTypes(); - String[] expectedTypes = - CacheMBean.class.getAnnotation(NotificationInfo.class).types(); - assert Arrays.equals(types, expectedTypes) : Arrays.toString(types); + // Test that inconsistent @NotificationInfo annotations produce an + // error. + try { + mbs.registerMBean(new Inconsistent(), on); + System.out.println(mbs.getMBeanInfo(on)); + assert false : "Inconsistent @NotificationInfo not detected"; + } catch (Exception e) { + System.out.println( + "Inconsistent @NotificationInfo correctly produced " + e); + } } } From 049436370fd35ebb2b294c37b5d28cc005ff456b Mon Sep 17 00:00:00 2001 From: Jean-Francois DeniseDescriptor constructor taking an XML String.
+ *Descriptor constructor taking an XML String or a + * fieldName=fieldValue format String. The String parameter is + * parsed as XML if it begins with a '<' character.
* *The format of the XML string is not defined, but an * implementation must ensure that the string returned by @@ -244,17 +245,20 @@ public class DescriptorSupport * programmer will have to reset or convert these fields * correctly.
* - * @param inStr An XML-formatted string used to populate this - * Descriptor. The format is not defined, but any + * @param inStr An XML-format or a fieldName=fieldValue formatted string + * used to populate this Descriptor. The XML format is not defined, but any * implementation must ensure that the string returned by * method {@link #toXMLString toXMLString} on an existing * descriptor can be used to instantiate an equivalent * descriptor when instantiated using this constructor. * - * @exception RuntimeOperationsException If the String inStr - * passed in parameter is null + * @exception RuntimeOperationsException If the String inStr passed in + * parameter is null or, when it is not an XML string, if the field name or + * field value is illegal. If inStr is not an XML string then it must + * contain an "=". "fieldValue", "fieldName", and "fieldValue" are illegal. + * FieldName cannot be empty. "fieldName=" will cause the value to be empty. * @exception XMLParseException XML parsing problem while parsing - * the input String + * the XML-format input String * @exception MBeanException Wraps a distributed communication Exception. */ /* At some stage we should rewrite this code to be cleverer. Using @@ -283,14 +287,27 @@ public class DescriptorSupport throw new RuntimeOperationsException(iae, msg); } + // parse parameter string into structures + + init(null); + + if(!inStr.startsWith("<")) { + parseNamesValues(inStr); + if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { + MODELMBEAN_LOGGER.logp(Level.FINEST, + DescriptorSupport.class.getName(), + "Descriptor(name=value)", "Exit"); + } + return; + } + final String lowerInStr = inStr.toLowerCase(); if (!lowerInStr.startsWith("Options to apply to an MBean proxy or to an instance of {@link * StandardMBean}.
diff --git a/jdk/src/share/classes/javax/management/MBeanAttributeInfo.java b/jdk/src/share/classes/javax/management/MBeanAttributeInfo.java index 2566c3e48a2..b4bcfaf4585 100644 --- a/jdk/src/share/classes/javax/management/MBeanAttributeInfo.java +++ b/jdk/src/share/classes/javax/management/MBeanAttributeInfo.java @@ -186,8 +186,10 @@ public class MBeanAttributeInfo extends MBeanFeatureInfo implements Cloneable { (getter != null), (setter != null), isIs(getter), - ImmutableDescriptor.union(Introspector.descriptorForElement(getter), - Introspector.descriptorForElement(setter))); + ImmutableDescriptor.union(Introspector. + descriptorForElement(getter, false), + Introspector.descriptorForElement(setter, + true))); } /** diff --git a/jdk/src/share/classes/javax/management/MBeanConstructorInfo.java b/jdk/src/share/classes/javax/management/MBeanConstructorInfo.java index 872c723e3af..e3e210318d8 100644 --- a/jdk/src/share/classes/javax/management/MBeanConstructorInfo.java +++ b/jdk/src/share/classes/javax/management/MBeanConstructorInfo.java @@ -67,7 +67,7 @@ public class MBeanConstructorInfo extends MBeanFeatureInfo implements Cloneable public MBeanConstructorInfo(String description, Constructor> constructor) { this(constructor.getName(), description, constructorSignature(constructor), - Introspector.descriptorForElement(constructor)); + Introspector.descriptorForElement(constructor, false)); } /** diff --git a/jdk/src/share/classes/javax/management/MBeanOperationInfo.java b/jdk/src/share/classes/javax/management/MBeanOperationInfo.java index 006fc345d93..c3ee5382612 100644 --- a/jdk/src/share/classes/javax/management/MBeanOperationInfo.java +++ b/jdk/src/share/classes/javax/management/MBeanOperationInfo.java @@ -113,7 +113,7 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { methodSignature(method), method.getReturnType().getName(), UNKNOWN, - Introspector.descriptorForElement(method)); + Introspector.descriptorForElement(method, false)); } /** diff --git a/jdk/test/javax/management/Introspector/ExceptionsDescriptorTest.java b/jdk/test/javax/management/Introspector/ExceptionsDescriptorTest.java new file mode 100644 index 00000000000..5b931ba7294 --- /dev/null +++ b/jdk/test/javax/management/Introspector/ExceptionsDescriptorTest.java @@ -0,0 +1,245 @@ +/* + * 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 %M% %I% + * @bug 6250014 + * @summary Test that Exceptions are added to the MbeanInfo + * @author Jean-Francois Denise + * @run main/othervm ExceptionsDescriptorTest + */ +import java.lang.management.ManagementFactory; +import java.util.HashSet; +import java.util.Set; +import javax.management.Descriptor; +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.ObjectName; + +public class ExceptionsDescriptorTest { + + private static final ObjectName OBJECT_NAME = ObjectName.valueOf(":type=Foo"); + final static String EXCEPTION_NAME = Exception.class.getName(); + final static String ILLEGAL_ARGUMENT_EXCEPTION_NAME = + IllegalArgumentException.class.getName(); + final static SetOptions to apply to an MBean proxy or to an instance of {@link * StandardMBean}.
diff --git a/jdk/src/share/classes/javax/management/MBeanServer.java b/jdk/src/share/classes/javax/management/MBeanServer.java index e6d79e5bd94..e82650288bb 100644 --- a/jdk/src/share/classes/javax/management/MBeanServer.java +++ b/jdk/src/share/classes/javax/management/MBeanServer.java @@ -351,11 +351,14 @@ public interface MBeanServer extends MBeanServerConnection { /** *Registers a pre-existing object as an MBean with the MBean - * server. If the object name given is null, the MBean must - * provide its own name by implementing the {@link + * server. If the object name given is null, the + * MBean must provide its own name in one or both of two ways: by implementing the {@link * javax.management.MBeanRegistration MBeanRegistration} interface * and returning the name from the {@link - * MBeanRegistration#preRegister preRegister} method.
+ * MBeanRegistration#preRegister preRegister} method; or by defining + * an {@code objectNameTemplate} field in its {@link Descriptor}, + * typically using the {@link ObjectNameTemplate @ObjectNameTemplate} + * annotation. * *If this method successfully registers an MBean, a notification * is sent as described above.
diff --git a/jdk/src/share/classes/javax/management/MBeanServerConnection.java b/jdk/src/share/classes/javax/management/MBeanServerConnection.java index fadebc10730..add96cfcb94 100644 --- a/jdk/src/share/classes/javax/management/MBeanServerConnection.java +++ b/jdk/src/share/classes/javax/management/MBeanServerConnection.java @@ -46,11 +46,14 @@ public interface MBeanServerConnection extends NotificationManager { * MBean server will use its {@link * javax.management.loading.ClassLoaderRepository Default Loader * Repository} to load the class of the MBean. An object name is - * associated to the MBean. If the object name given is null, the - * MBean must provide its own name by implementing the {@link + * associated with the MBean. If the object name given is null, the + * MBean must provide its own name in one or both of two ways: by implementing the {@link * javax.management.MBeanRegistration MBeanRegistration} interface * and returning the name from the {@link - * MBeanRegistration#preRegister preRegister} method. + * MBeanRegistration#preRegister preRegister} method; or by defining + * an {@code objectNameTemplate} field in its {@link Descriptor}, + * typically using the {@link ObjectNameTemplate @ObjectNameTemplate} + * annotation. * *This method is equivalent to {@link * #createMBean(String,ObjectName,Object[],String[]) @@ -117,13 +120,16 @@ public interface MBeanServerConnection extends NotificationManager { /** *
Instantiates and registers an MBean in the MBean server. The * class loader to be used is identified by its object name. An - * object name is associated to the MBean. If the object name of + * object name is associated with the MBean. If the object name of * the loader is null, the ClassLoader that loaded the MBean - * server will be used. If the MBean's object name given is null, - * the MBean must provide its own name by implementing the {@link + * server will be used. If the object name given is null, the + * MBean must provide its own name in one or both of two ways: by implementing the {@link * javax.management.MBeanRegistration MBeanRegistration} interface * and returning the name from the {@link - * MBeanRegistration#preRegister preRegister} method.
+ * MBeanRegistration#preRegister preRegister} method; or by defining + * an {@code objectNameTemplate} field in its {@link Descriptor}, + * typically using the {@link ObjectNameTemplate @ObjectNameTemplate} + * annotation. * *This method is equivalent to {@link * #createMBean(String,ObjectName,ObjectName,Object[],String[]) @@ -198,11 +204,14 @@ public interface MBeanServerConnection extends NotificationManager { * MBean server will use its {@link * javax.management.loading.ClassLoaderRepository Default Loader * Repository} to load the class of the MBean. An object name is - * associated to the MBean. If the object name given is null, the - * MBean must provide its own name by implementing the {@link + * associated with the MBean. If the object name given is null, the + * MBean must provide its own name in one or both of two ways: by implementing the {@link * javax.management.MBeanRegistration MBeanRegistration} interface * and returning the name from the {@link - * MBeanRegistration#preRegister preRegister} method. + * MBeanRegistration#preRegister preRegister} method; or by defining + * an {@code objectNameTemplate} field in its {@link Descriptor}, + * typically using the {@link ObjectNameTemplate @ObjectNameTemplate} + * annotation.
* * @param className The class name of the MBean to be instantiated. * @param name The object name of the MBean. May be null. @@ -267,15 +276,18 @@ public interface MBeanServerConnection extends NotificationManager { NotCompliantMBeanException, IOException; /** - * Instantiates and registers an MBean in the MBean server. The + *Instantiates and registers an MBean in the MBean server. The * class loader to be used is identified by its object name. An - * object name is associated to the MBean. If the object name of + * object name is associated with the MBean. If the object name of * the loader is not specified, the ClassLoader that loaded the - * MBean server will be used. If the MBean object name given is - * null, the MBean must provide its own name by implementing the - * {@link javax.management.MBeanRegistration MBeanRegistration} - * interface and returning the name from the {@link - * MBeanRegistration#preRegister preRegister} method. + * MBean server will be used. If the object name given is null, the + * MBean must provide its own name in one or both of two ways: by implementing the {@link + * javax.management.MBeanRegistration MBeanRegistration} interface + * and returning the name from the {@link + * MBeanRegistration#preRegister preRegister} method; or by defining + * an {@code objectNameTemplate} field in its {@link Descriptor}, + * typically using the {@link ObjectNameTemplate @ObjectNameTemplate} + * annotation.
* * @param className The class name of the MBean to be instantiated. * @param name The object name of the MBean. May be null. diff --git a/jdk/src/share/classes/javax/management/ObjectNameTemplate.java b/jdk/src/share/classes/javax/management/ObjectNameTemplate.java new file mode 100644 index 00000000000..97e0c900fb3 --- /dev/null +++ b/jdk/src/share/classes/javax/management/ObjectNameTemplate.java @@ -0,0 +1,131 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package javax.management; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to allow an MBean to provide its name. + * This annotation can be used on the following types: + *The value of this annotation is used to build the ObjectName
+ * when instances of the annotated type are registered in
+ * an MBeanServer and no explicit name is given to the
+ * {@code createMBean} or {@code registerMBean} method (the {@code ObjectName}
+ * is {@code null}).
For Dynamic MBeans, which define their own {@code MBeanInfo}, you can + * produce the same effect as this annotation by including a field + * {@code objectNameTemplate} + * in the {@link Descriptor} for the {@code MBeanInfo} returned by + * {@link DynamicMBean#getMBeanInfo()}.
+ * + *For Standard MBeans and MXBeans, this annotation automatically produces + * an {@code objectNameTemplate} field in the {@code Descriptor}.
+ * + *The template can contain variables so that the name of the MBean
+ * depends on the value of one or more of its attributes.
+ * A variable that identifies an MBean attribute is of the form
+ * {attribute name}. For example, to make an MBean name
+ * depend on the Name attribute, use the variable
+ * {Name}. Attribute names are case sensitive.
+ * Naming attributes can be of any type. The String returned by
+ * toString() is included in the constructed name.
If you need the attribute value to be quoted
+ * by a call to {@link ObjectName#quote(String) ObjectName.quote},
+ * surround the variable with quotes. Quoting only applies to key values.
+ * For example, @ObjectNameTemplate("java.lang:type=MemoryPool,name=\"{Name}\""),
+ * quotes the Name attribute value. You can notice the "\"
+ * character needed to escape a quote within a String. A name
+ * produced by this template might look like
+ * {@code java.lang:type=MemoryPool,name="Code Cache"}.
Variables can be used anywhere in the String.
+ * Be sure to make the template derived name comply with
+ * {@link ObjectName ObjectName} syntax.
If an MBean is registered with a null name and it implements
+ * {@link javax.management.MBeanRegistration MBeanRegistration}, then
+ * the computed name is provided to the preRegister method.
+ * Similarly,
+ * if the MBean uses resource
+ * injection to discover its name, it is the computed name that will
+ * be injected.
All of the above can be used with the {@link StandardMBean} class and + * the annotation is effective in that case too.
+ *If any exception occurs (such as unknown attribute, invalid syntax or
+ * exception
+ * thrown by the MBean) when the name is computed it is wrapped in a
+ * NotCompliantMBeanException.
Some ObjectName template examples: + *
Name attribute is retrieved to compose the name
+ * key value.InstanceName and InstanceId attributes are
+ * retrieved to compose respectively
+ * the name and id key values.ComplexName attribute is retrieved to compose the
+ * name key quoted value.TypeKey attribute is retrieved to compose the
+ * first key name.Domain attribute is retrieved to compose the
+ * management domain.Naming attribute is retrieved to compose the
+ * complete name.Construct an {@code Options} object where all options have @@ -177,15 +178,56 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { this.wrappedVisible = visible; } - // Canonical objects for each of (MXBean,!MXBean) x (WVisible,!WVisible) + /** + *
Defines whether the {@link MBeanRegistration MBeanRegistration} + * callbacks are forwarded to the wrapped object.
+ * + *If this option is true, then + * {@link #preRegister(MBeanServer, ObjectName) preRegister}, + * {@link #postRegister(Boolean) postRegister}, + * {@link #preDeregister preDeregister} and + * {@link #postDeregister postDeregister} methods are forwarded + * to the wrapped object, in addition to the behaviour specified + * for the StandardMBean instance itself. + * The default value is false for compatibility reasons, but true + * is a better value for most new code.
+ * + * @return true if theMBeanRegistration callbacks
+ * are forwarded to the wrapped object.
+ */
+ public boolean isMBeanRegistrationForwarded() {
+ return this.forwardRegistration;
+ }
+
+ /**
+ * Set the + * {@link #isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option to the given value.
+ * @param forward the new value. + */ + public void setMBeanRegistrationForwarded(boolean forward) { + this.forwardRegistration = forward; + } + + // Canonical objects for each of + // (MXBean,!MXBean) x (WVisible,!WVisible) x (Forward,!Forward) private static final Options[] CANONICALS = { new Options(), new Options(), new Options(), new Options(), + new Options(), new Options(), new Options(), new Options(), }; static { CANONICALS[1].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); CANONICALS[2].setWrappedObjectVisible(true); CANONICALS[3].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); CANONICALS[3].setWrappedObjectVisible(true); + CANONICALS[4].setMBeanRegistrationForwarded(true); + CANONICALS[5].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); + CANONICALS[5].setMBeanRegistrationForwarded(true); + CANONICALS[6].setWrappedObjectVisible(true); + CANONICALS[6].setMBeanRegistrationForwarded(true); + CANONICALS[7].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT); + CANONICALS[7].setWrappedObjectVisible(true); + CANONICALS[7].setMBeanRegistrationForwarded(true); } @Override MBeanOptions[] canonicals() { @@ -195,7 +237,8 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { @Override boolean same(MBeanOptions opts) { return (super.same(opts) && opts instanceof Options && - ((Options) opts).wrappedVisible == wrappedVisible); + ((Options) opts).wrappedVisible == wrappedVisible && + ((Options) opts).forwardRegistration ==forwardRegistration); } } @@ -477,7 +520,9 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { * * @exception IllegalArgumentException if the given * implementation is null. - * + * @exception IllegalStateException if the + * {@link Options#isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option is true. * @exception NotCompliantMBeanException if the given * implementation does not implement the * Standard MBean (or MXBean) interface that was @@ -490,6 +535,12 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { if (implementation == null) throw new IllegalArgumentException("implementation is null"); + + if(options instanceof Options && + ((Options) options).isMBeanRegistrationForwarded()) + throw new IllegalStateException("Implementation can't be changed " + + "because MBeanRegistrationForwarded option is true"); + setImplementation2(implementation); } @@ -1273,10 +1324,14 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { * registered in the MBean server. * *The default implementation of this method returns the {@code name} - * parameter. It does nothing else for - * Standard MBeans. For MXBeans, it records the {@code MBeanServer} - * and {@code ObjectName} parameters so they can be used to translate - * inter-MXBean references.
+ * parameter. If the + * {@link Options#isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option is set to true, then this method is forwarded to the object + * returned by the {@link #getImplementation getImplementation()} method. + * The name returned by this call is then returned by this method. + * It does nothing else for Standard MBeans. For MXBeans, it records + * the {@code MBeanServer} and {@code ObjectName} parameters so they can + * be used to translate inter-MXBean references. * *It is good practice for a subclass that overrides this method * to call the overridden method via {@code super.preRegister(...)}. @@ -1311,6 +1366,11 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { */ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + // Forward preRegister before to call register and + // inject parameters. + if(shouldForwardMBeanRegistration()) + name = ((MBeanRegistration)getImplementation()). + preRegister(server, name); mbean.register(server, name); MBeanInjector.inject(mbean.getWrappedObject(), server, name); return name; @@ -1320,7 +1380,11 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { *
Allows the MBean to perform any operations needed after having been * registered in the MBean server or after the registration has failed.
* - *The default implementation of this method does nothing for + *
If the + * {@link Options#isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option is set to true, then this method is forwarded to the object + * returned by the {@link #getImplementation getImplementation()} method. + * The default implementation of this method does nothing else for * Standard MBeans. For MXBeans, it undoes any work done by * {@link #preRegister preRegister} if registration fails.
* @@ -1338,16 +1402,24 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { public void postRegister(Boolean registrationDone) { if (!registrationDone) mbean.unregister(); + if(shouldForwardMBeanRegistration()) + ((MBeanRegistration)getImplementation()). + postRegister(registrationDone); } /** *Allows the MBean to perform any operations it needs before * being unregistered by the MBean server.
* - *The default implementation of this method does nothing.
+ *If the + * {@link Options#isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option is set to true, then this method is forwarded to the object + * returned by the {@link #getImplementation getImplementation()} method. + * Other than that, the default implementation of this method does nothing. + *
* *It is good practice for a subclass that overrides this method - * to call the overridden method via {@code super.preDeegister(...)}.
+ * to call the overridden method via {@code super.preDeregister(...)}. * * @throws Exception no checked exceptions are throw by this method * but {@code Exception} is declared so that subclasses can override @@ -1356,13 +1428,19 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { * @since 1.6 */ public void preDeregister() throws Exception { + if(shouldForwardMBeanRegistration()) + ((MBeanRegistration)getImplementation()).preDeregister(); } /** *Allows the MBean to perform any operations needed after having been * unregistered in the MBean server.
* - *The default implementation of this method does nothing for + *
If the + * {@link Options#isMBeanRegistrationForwarded MBeanRegistrationForwarded} + * option is set to true, then this method is forwarded to the object + * returned by the {@link #getImplementation getImplementation()} method. + * The default implementation of this method does nothing else for * Standard MBeans. For MXBeans, it removes any information that * was recorded by the {@link #preRegister preRegister} method.
* @@ -1375,8 +1453,15 @@ public class StandardMBean implements DynamicWrapperMBean, MBeanRegistration { */ public void postDeregister() { mbean.unregister(); + if(shouldForwardMBeanRegistration()) + ((MBeanRegistration)getImplementation()).postDeregister(); } + private boolean shouldForwardMBeanRegistration() { + return (getImplementation() instanceof MBeanRegistration) && + (options instanceof Options && + ((Options) options).isMBeanRegistrationForwarded()); + } // // MBeanInfo immutability // diff --git a/jdk/src/share/classes/javax/management/monitor/MonitorNotification.java b/jdk/src/share/classes/javax/management/monitor/MonitorNotification.java index 744d7434f1f..f0d65c6da17 100644 --- a/jdk/src/share/classes/javax/management/monitor/MonitorNotification.java +++ b/jdk/src/share/classes/javax/management/monitor/MonitorNotification.java @@ -201,7 +201,7 @@ public class MonitorNotification extends javax.management.Notification { * @param derGauge The derived gauge. * @param trigger The threshold/string (depending on the monitor type) that triggered the notification. */ - MonitorNotification(String type, Object source, long sequenceNumber, long timeStamp, String msg, + public MonitorNotification(String type, Object source, long sequenceNumber, long timeStamp, String msg, ObjectName obsObj, String obsAtt, Object derGauge, Object trigger) { super(type, source, sequenceNumber, timeStamp, msg); diff --git a/jdk/test/javax/management/monitor/InstantiateMonitorNotificationTest.java b/jdk/test/javax/management/monitor/InstantiateMonitorNotificationTest.java new file mode 100644 index 00000000000..ae16858d241 --- /dev/null +++ b/jdk/test/javax/management/monitor/InstantiateMonitorNotificationTest.java @@ -0,0 +1,52 @@ + +import javax.management.ObjectName; +import javax.management.monitor.MonitorNotification; + +/* + * 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 6373143 + * @summary Test MonitorNotification public constructor + * @author JFDenise + * @run clean InstantiateMonitorNotificationTest + * @run build InstantiateMonitorNotificationTest + * @run main InstantiateMonitorNotificationTest + */ + +public class InstantiateMonitorNotificationTest { + + public static void main(String[] args) throws Exception { + MonitorNotification notif = new MonitorNotification("com.foo.test", + ObjectName.valueOf(":type=Monitor"), + 999, + 999, + "A message", + ObjectName.valueOf(":type=Observed"), + "MyAttribute", + Integer.valueOf(14), + Integer.valueOf(15)); + System.out.println("Test passed"); + } +} diff --git a/jdk/test/javax/management/standardmbean/RegistrationTest.java b/jdk/test/javax/management/standardmbean/RegistrationTest.java new file mode 100644 index 00000000000..9ca0c464f00 --- /dev/null +++ b/jdk/test/javax/management/standardmbean/RegistrationTest.java @@ -0,0 +1,91 @@ +/* + * 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 6450834 + * @summary Forward MBeanRegistration calls + * @author JF Denise + * @run main RegistrationTest + */ + +import java.io.Serializable; +import java.lang.management.ManagementFactory; +import javax.management.*; + +public class RegistrationTest { + static boolean preRegisterCalled; + static boolean postRegisterCalled; + static boolean preDeregisterCalled; + static boolean postDeregisterCalled; + + static void checkResult(boolean expected) throws Exception { + if((preRegisterCalled != expected || + postRegisterCalled != expected || + preDeregisterCalled != expected || + postDeregisterCalled != expected)) + throw new Exception("Mismatch preRegisterCalled = " + + preRegisterCalled + ", postRegisterCalled = " + + postRegisterCalled + ", preDeregisterCalled = " + + preDeregisterCalled + ", postDeregisterCalled = " + + postDeregisterCalled); + } + static class Wrapped implements MBeanRegistration,Serializable { + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + preRegisterCalled = true; + return name; + } + + public void postRegister(Boolean registrationDone) { + postRegisterCalled = true; + } + + public void preDeregister() throws Exception { + preDeregisterCalled = true; + } + + public void postDeregister() { + postDeregisterCalled = true; + } + + } + + public static void main(String[] args) throws Exception { + StandardMBean std = new StandardMBean(new Wrapped(), + Serializable.class); + ObjectName name = ObjectName.valueOf(":type=Test"); + ManagementFactory.getPlatformMBeanServer().registerMBean(std,name); + ManagementFactory.getPlatformMBeanServer().unregisterMBean(name); + checkResult(false); + StandardMBean.Options opt = new StandardMBean.Options(); + opt.setMBeanRegistrationForwarded(true); + std = new StandardMBean(new Wrapped(), + Serializable.class, opt ); + ManagementFactory.getPlatformMBeanServer().registerMBean(std,name); + ManagementFactory.getPlatformMBeanServer().unregisterMBean(name); + checkResult(true); + System.out.println("Test OK"); + } +} From 85e1e1e290ef9d63bc05988e8796cce21826d7f3 Mon Sep 17 00:00:00 2001 From: Jean-Francois DeniseMethods
+ * located in the mbeanInterface MBean interface that
+ * correspond to the attr MBeanAttributeInfo
+ * parameter.
+ * @param mbeanInterface the management interface.
+ * Can be a standard MBean or MXBean interface, or a Java class
+ * annotated with {@link MBean @MBean} or {@link MXBean @MXBean}.
+ * @param attr The attribute we want the accessors for.
+ * @return The set of accessors.
+ * @throws java.lang.NoSuchMethodException if no accessor exists
+ * for the given {@link MBeanAttributeInfo MBeanAttributeInfo}.
+ * @throws java.lang.IllegalArgumentException if at least one
+ * of the two parameters is null.
+ * @throws java.lang.ClassNotFoundException if the class named in the
+ * attribute type is not found.
+ * @throws java.lang.SecurityException if this exception is
+ * encountered while introspecting the MBean interface.
+ */
+ public static SetMethod
+ * located in the mbeanInterface MBean interface that
+ * corresponds to the provided op
+ * MBeanOperationInfo parameter.
+ * @param mbeanInterface the management interface.
+ * Can be a standard MBean or MXBean interface, or a Java class
+ * annotated with {@link MBean @MBean} or {@link MXBean @MXBean}.
+ * @param op The operation we want the method for.
+ * @return the method corresponding to the provided MBeanOperationInfo.
+ * @throws java.lang.NoSuchMethodException if no method exists
+ * for the given {@link MBeanOperationInfo MBeanOperationInfo}.
+ * @throws java.lang.IllegalArgumentException if at least one
+ * of the two parameters is null.
+ * @throws java.lang.ClassNotFoundException if one of the
+ * classes named in the operation signature array is not found.
+ * @throws java.lang.SecurityException if this exception is
+ * encountered while introspecting the MBean interface.
+ */
+ public static Method findOperationMethod(Class> mbeanInterface,
+ MBeanOperationInfo op)
+ throws ClassNotFoundException, NoSuchMethodException {
+ if (mbeanInterface == null || op == null) {
+ throw new IllegalArgumentException("mbeanInterface or op " +
+ "parameter is null");
+ }
+ ListAllows the MBean to perform any operations it needs before
* being registered in the MBean server. If the name of the MBean
diff --git a/jdk/test/javax/management/standardmbean/FindMethodTest.java b/jdk/test/javax/management/standardmbean/FindMethodTest.java
new file mode 100644
index 00000000000..b568d70ac88
--- /dev/null
+++ b/jdk/test/javax/management/standardmbean/FindMethodTest.java
@@ -0,0 +1,384 @@
+/*
+ * 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 6287328
+ * @summary Add methods to StandardMBean to retrieve a method based on
+ * MBean{Attribute|Operation}Info
+ * @author Jean-Francois Denise
+ * @run main FindMethodTest
+ */
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.management.MBean;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanServer;
+import javax.management.ManagedAttribute;
+import javax.management.ManagedOperation;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+public class FindMethodTest {
+
+ private static MBeanServer server =
+ ManagementFactory.getPlatformMBeanServer();
+
+ private static Map