提交 918b0732 编写于 作者: E emcmanus

6774918: @NotificationInfo is ineffective on MBeans that cannot send notifications

Reviewed-by: jfdenise
上级 7bc415b1
...@@ -47,6 +47,10 @@ import java.util.List; ...@@ -47,6 +47,10 @@ import java.util.List;
import javax.management.SendNotification; import javax.management.SendNotification;
public class MBeanInjector { public class MBeanInjector {
// There are no instances of this class
private MBeanInjector() {
}
private static Class<?>[] injectedClasses = { private static Class<?>[] injectedClasses = {
MBeanServer.class, ObjectName.class, SendNotification.class, MBeanServer.class, ObjectName.class, SendNotification.class,
}; };
......
...@@ -44,7 +44,6 @@ import javax.management.Descriptor; ...@@ -44,7 +44,6 @@ import javax.management.Descriptor;
import javax.management.ImmutableDescriptor; import javax.management.ImmutableDescriptor;
import javax.management.IntrospectionException; import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException; import javax.management.InvalidAttributeValueException;
import javax.management.JMX;
import javax.management.MBean; import javax.management.MBean;
import javax.management.MBeanAttributeInfo; import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo; import javax.management.MBeanConstructorInfo;
...@@ -538,6 +537,14 @@ public abstract class MBeanIntrospector<M> { ...@@ -538,6 +537,14 @@ public abstract class MBeanIntrospector<M> {
} }
} }
/*
* Return the array of MBeanNotificationInfo for the given MBean object.
* If the object implements NotificationBroadcaster and its
* getNotificationInfo() method returns a non-empty array, then that
* is the result. Otherwise, if the object has a @NotificationInfo
* or @NotificationInfos annotation, then its contents form the result.
* Otherwise, the result is null.
*/
static MBeanNotificationInfo[] findNotifications(Object moi) { static MBeanNotificationInfo[] findNotifications(Object moi) {
if (moi instanceof NotificationBroadcaster) { if (moi instanceof NotificationBroadcaster) {
MBeanNotificationInfo[] mbn = MBeanNotificationInfo[] mbn =
...@@ -553,6 +560,13 @@ public abstract class MBeanIntrospector<M> { ...@@ -553,6 +560,13 @@ public abstract class MBeanIntrospector<M> {
} }
return result; return result;
} }
} else {
try {
if (!MBeanInjector.injectsSendNotification(moi))
return null;
} catch (NotCompliantMBeanException e) {
throw new RuntimeException(e);
}
} }
return findNotificationsFromAnnotations(moi.getClass()); return findNotificationsFromAnnotations(moi.getClass());
} }
......
...@@ -44,7 +44,13 @@ import java.lang.annotation.Target; ...@@ -44,7 +44,13 @@ import java.lang.annotation.Target;
* "com.example.notifs.destroy"}) * "com.example.notifs.destroy"})
* public interface CacheMBean {...} * public interface CacheMBean {...}
* *
* public class Cache implements CacheMBean {...} * public class Cache
* extends NotificationBroadcasterSupport implements CacheMBean {
* public Cache() {
* super(); // do not supply any MBeanNotificationInfo[]
* }
* ...
* }
* </pre> * </pre>
* *
* <pre> * <pre>
...@@ -52,7 +58,11 @@ import java.lang.annotation.Target; ...@@ -52,7 +58,11 @@ import java.lang.annotation.Target;
* {@link MBean @MBean} * {@link MBean @MBean}
* {@code @NotificationInfo}(types={"com.example.notifs.create", * {@code @NotificationInfo}(types={"com.example.notifs.create",
* "com.example.notifs.destroy"}) * "com.example.notifs.destroy"})
* public class Cache {...} * public class Cache {
* <a href="MBeanRegistration.html#injection">{@code @Resource}</a>
* private volatile SendNotification sendNotification;
* ...
* }
* </pre> * </pre>
* *
* <p>Each {@code @NotificationInfo} produces an {@link * <p>Each {@code @NotificationInfo} produces an {@link
...@@ -64,6 +74,13 @@ import java.lang.annotation.Target; ...@@ -64,6 +74,13 @@ import java.lang.annotation.Target;
* several {@code @NotificationInfo} annotations into a containing * several {@code @NotificationInfo} annotations into a containing
* {@link NotificationInfos @NotificationInfos} annotation. * {@link NotificationInfos @NotificationInfos} annotation.
* *
* <p>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.</p>
*
* <p>The {@code NotificationInfo} and {@code NotificationInfos} * <p>The {@code NotificationInfo} and {@code NotificationInfos}
* annotations can be applied to the MBean implementation class, or to * annotations can be applied to the MBean implementation class, or to
* any parent class or interface. These annotations on a class take * any parent class or interface. These annotations on a class take
...@@ -71,7 +88,8 @@ import java.lang.annotation.Target; ...@@ -71,7 +88,8 @@ import java.lang.annotation.Target;
* If an MBean does not have these annotations on its class or any * If an MBean does not have these annotations on its class or any
* superclass, then superinterfaces are examined. It is an error for * superclass, then superinterfaces are examined. It is an error for
* more than one superinterface to have these annotations, unless one * more than one superinterface to have these annotations, unless one
* of them is a child of all the others.</p> * of them is a descendant of all the others; registering such an erroneous
* MBean will cause a {@link NotCompliantMBeanException}.</p>
*/ */
@Documented @Documented
@Inherited @Inherited
......
...@@ -38,19 +38,34 @@ import javax.management.AttributeChangeNotification; ...@@ -38,19 +38,34 @@ import javax.management.AttributeChangeNotification;
import javax.management.Description; import javax.management.Description;
import javax.management.Descriptor; import javax.management.Descriptor;
import javax.management.ImmutableDescriptor; import javax.management.ImmutableDescriptor;
import javax.management.ListenerNotFoundException;
import javax.management.MBean; import javax.management.MBean;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo; import javax.management.MBeanNotificationInfo;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.MXBean; import javax.management.MXBean;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter;
import javax.management.NotificationInfo; import javax.management.NotificationInfo;
import javax.management.NotificationInfos; import javax.management.NotificationInfos;
import javax.management.NotificationListener;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.SendNotification; import javax.management.SendNotification;
public class AnnotatedNotificationInfoTest { 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. // is correctly derived from @NotificationInfo.
// Every static field called mbean* is expected to be an MBean // Every static field called mbean* is expected to be an MBean
// with a single MBeanNotificationInfo that has the same value // with a single MBeanNotificationInfo that has the same value
...@@ -254,11 +269,48 @@ public class AnnotatedNotificationInfoTest { ...@@ -254,11 +269,48 @@ public class AnnotatedNotificationInfoTest {
private static Object mbeanMXBean2 = new MXBean2(); private static Object mbeanMXBean2 = new MXBean2();
// Classes for the second test. This tests the simplest case, which is // Test that @NotificationInfo and @NotificationInfos are ignored if
// the first example in the javadoc for @NotificationInfo. Notice that // the MBean returns a non-empty MBeanNotificationInfo[] from its
// this MBean is not a NotificationBroadcaster and does not inject a // NotificationBroadcaster.getNotifications() implementation.
// SendNotification! That should possibly be an error, but it's currently
// allowed by the spec. @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", @NotificationInfo(types={"com.example.notifs.create",
"com.example.notifs.destroy"}) "com.example.notifs.destroy"})
public static interface CacheMBean { public static interface CacheMBean {
...@@ -271,6 +323,73 @@ public class AnnotatedNotificationInfoTest { ...@@ -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 { public static void main(String[] args) throws Exception {
if (!AnnotatedNotificationInfoTest.class.desiredAssertionStatus()) if (!AnnotatedNotificationInfoTest.class.desiredAssertionStatus())
throw new Exception("Test must be run with -ea"); throw new Exception("Test must be run with -ea");
...@@ -278,37 +397,46 @@ public class AnnotatedNotificationInfoTest { ...@@ -278,37 +397,46 @@ public class AnnotatedNotificationInfoTest {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName on = new ObjectName("a:b=c"); 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..."); System.out.println("Testing MBeans...");
for (Field mbeanField : for (Field mbeanField :
AnnotatedNotificationInfoTest.class.getDeclaredFields()) { 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; continue;
System.out.println("..." + mbeanField.getName()); System.out.println("..." + mbeanField.getName());
Object mbean = mbeanField.get(null); Object mbean = mbeanField.get(null);
mbs.registerMBean(mbean, on); mbs.registerMBean(mbean, on);
MBeanInfo mbi = mbs.getMBeanInfo(on); MBeanInfo mbi = mbs.getMBeanInfo(on);
MBeanNotificationInfo[] mbnis = mbi.getNotifications(); MBeanNotificationInfo[] mbnis = mbi.getNotifications();
assert mbnis.length == 1 : mbnis.length; if (notifier) {
assert mbnis[0].equals(expected) : mbnis[0]; 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.unregisterMBean(on);
} }
mbs.registerMBean(new Cache(), on); // Test that inconsistent @NotificationInfo annotations produce an
MBeanInfo mbi = mbs.getMBeanInfo(on); // error.
MBeanNotificationInfo[] mbnis = mbi.getNotifications(); try {
assert mbnis.length == 1 : mbnis.length; mbs.registerMBean(new Inconsistent(), on);
String[] types = mbnis[0].getNotifTypes(); System.out.println(mbs.getMBeanInfo(on));
String[] expectedTypes = assert false : "Inconsistent @NotificationInfo not detected";
CacheMBean.class.getAnnotation(NotificationInfo.class).types(); } catch (Exception e) {
assert Arrays.equals(types, expectedTypes) : Arrays.toString(types); System.out.println(
"Inconsistent @NotificationInfo correctly produced " + e);
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册