Browse Source

Polishing

Issue: SPR-8045
(cherry picked from commit 7eee7d2)
pull/689/head
Juergen Hoeller 12 years ago
parent
commit
cec3ffa3ca
  1. 81
      spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java
  2. 7
      spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java
  3. 57
      spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java

81
spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -96,8 +96,8 @@ import org.springframework.util.ObjectUtils;
* @see org.springframework.jmx.export.assembler.MBeanInfoAssembler * @see org.springframework.jmx.export.assembler.MBeanInfoAssembler
* @see MBeanExporterListener * @see MBeanExporterListener
*/ */
public class MBeanExporter extends MBeanRegistrationSupport public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExportOperations,
implements MBeanExportOperations, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
/** /**
* Autodetection mode indicating that no autodetection should be used. * Autodetection mode indicating that no autodetection should be used.
@ -146,6 +146,12 @@ public class MBeanExporter extends MBeanRegistrationSupport
/** Whether to eagerly initialize candidate beans when autodetecting MBeans */ /** Whether to eagerly initialize candidate beans when autodetecting MBeans */
private boolean allowEagerInit = false; private boolean allowEagerInit = false;
/** Stores the MBeanInfoAssembler to use for this exporter */
private MBeanInfoAssembler assembler = new SimpleReflectiveMBeanInfoAssembler();
/** The strategy to use for creating ObjectNames for an object */
private ObjectNamingStrategy namingStrategy = new KeyNamingStrategy();
/** Indicates whether Spring should modify generated ObjectNames */ /** Indicates whether Spring should modify generated ObjectNames */
private boolean ensureUniqueRuntimeObjectNames = true; private boolean ensureUniqueRuntimeObjectNames = true;
@ -165,12 +171,6 @@ public class MBeanExporter extends MBeanRegistrationSupport
private final Map<NotificationListenerBean, ObjectName[]> registeredNotificationListeners = private final Map<NotificationListenerBean, ObjectName[]> registeredNotificationListeners =
new LinkedHashMap<NotificationListenerBean, ObjectName[]>(); new LinkedHashMap<NotificationListenerBean, ObjectName[]>();
/** Stores the MBeanInfoAssembler to use for this exporter */
private MBeanInfoAssembler assembler = new SimpleReflectiveMBeanInfoAssembler();
/** The strategy to use for creating ObjectNames for an object */
private ObjectNamingStrategy namingStrategy = new KeyNamingStrategy();
/** Stores the ClassLoader to use for generating lazy-init proxies */ /** Stores the ClassLoader to use for generating lazy-init proxies */
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@ -283,22 +283,6 @@ public class MBeanExporter extends MBeanRegistrationSupport
this.namingStrategy = namingStrategy; this.namingStrategy = namingStrategy;
} }
/**
* Set the {@code MBeanExporterListener}s that should be notified
* of MBean registration and unregistration events.
* @see MBeanExporterListener
*/
public void setListeners(MBeanExporterListener[] listeners) {
this.listeners = listeners;
}
/**
* Set the list of names for beans that should be excluded from autodetection.
*/
public void setExcludedBeans(String[] excludedBeans) {
this.excludedBeans = (excludedBeans != null ? new HashSet<String>(Arrays.asList(excludedBeans)) : null);
}
/** /**
* Indicates whether Spring should ensure that {@link ObjectName ObjectNames} * Indicates whether Spring should ensure that {@link ObjectName ObjectNames}
* generated by the configured {@link ObjectNamingStrategy} for * generated by the configured {@link ObjectNamingStrategy} for
@ -324,6 +308,22 @@ public class MBeanExporter extends MBeanRegistrationSupport
this.exposeManagedResourceClassLoader = exposeManagedResourceClassLoader; this.exposeManagedResourceClassLoader = exposeManagedResourceClassLoader;
} }
/**
* Set the list of names for beans that should be excluded from autodetection.
*/
public void setExcludedBeans(String... excludedBeans) {
this.excludedBeans = (excludedBeans != null ? new HashSet<String>(Arrays.asList(excludedBeans)) : null);
}
/**
* Set the {@code MBeanExporterListener}s that should be notified
* of MBean registration and unregistration events.
* @see MBeanExporterListener
*/
public void setListeners(MBeanExporterListener... listeners) {
this.listeners = listeners;
}
/** /**
* Set the {@link NotificationListenerBean NotificationListenerBeans} * Set the {@link NotificationListenerBean NotificationListenerBeans}
* containing the * containing the
@ -332,7 +332,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
* @see #setNotificationListenerMappings(java.util.Map) * @see #setNotificationListenerMappings(java.util.Map)
* @see NotificationListenerBean * @see NotificationListenerBean
*/ */
public void setNotificationListeners(NotificationListenerBean[] notificationListeners) { public void setNotificationListeners(NotificationListenerBean... notificationListeners) {
this.notificationListeners = notificationListeners; this.notificationListeners = notificationListeners;
} }
@ -398,16 +398,17 @@ public class MBeanExporter extends MBeanRegistrationSupport
//--------------------------------------------------------------------- //---------------------------------------------------------------------
/** /**
* Start bean registration automatically when deployed in an * Kick off bean registration automatically when deployed in an
* {@code ApplicationContext}. * {@code ApplicationContext}.
* @see #registerBeans() * @see #registerBeans()
*/ */
public void afterPropertiesSet() { public void afterPropertiesSet() {
// If no server was provided then try to find one. This is useful in an environment // If no server was provided then try to find one. This is useful in an environment
// such as JDK 1.5, Tomcat or JBoss where there is already an MBeanServer loaded. // where there is already an MBeanServer loaded.
if (this.server == null) { if (this.server == null) {
this.server = JmxUtils.locateMBeanServer(); this.server = JmxUtils.locateMBeanServer();
} }
try { try {
logger.info("Registering beans for JMX exposure on startup"); logger.info("Registering beans for JMX exposure on startup");
registerBeans(); registerBeans();
@ -512,7 +513,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
} }
if (mode == AUTODETECT_MBEAN || mode == AUTODETECT_ALL) { if (mode == AUTODETECT_MBEAN || mode == AUTODETECT_ALL) {
// Autodetect any beans that are already MBeans. // Autodetect any beans that are already MBeans.
this.logger.debug("Autodetecting user-defined JMX MBeans"); logger.debug("Autodetecting user-defined JMX MBeans");
autodetectMBeans(); autodetectMBeans();
} }
// Allow the assembler a chance to vote for bean inclusion. // Allow the assembler a chance to vote for bean inclusion.
@ -739,7 +740,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
* @return whether the class qualifies as an MBean * @return whether the class qualifies as an MBean
* @see org.springframework.jmx.support.JmxUtils#isMBean(Class) * @see org.springframework.jmx.support.JmxUtils#isMBean(Class)
*/ */
protected boolean isMBean(Class beanClass) { protected boolean isMBean(Class<?> beanClass) {
return JmxUtils.isMBean(beanClass); return JmxUtils.isMBean(beanClass);
} }
@ -753,24 +754,24 @@ public class MBeanExporter extends MBeanRegistrationSupport
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected DynamicMBean adaptMBeanIfPossible(Object bean) throws JMException { protected DynamicMBean adaptMBeanIfPossible(Object bean) throws JMException {
Class targetClass = AopUtils.getTargetClass(bean); Class<?> targetClass = AopUtils.getTargetClass(bean);
if (targetClass != bean.getClass()) { if (targetClass != bean.getClass()) {
Class ifc = JmxUtils.getMXBeanInterface(targetClass); Class<?> ifc = JmxUtils.getMXBeanInterface(targetClass);
if (ifc != null) { if (ifc != null) {
if (!(ifc.isInstance(bean))) { if (!ifc.isInstance(bean)) {
throw new NotCompliantMBeanException("Managed bean [" + bean + throw new NotCompliantMBeanException("Managed bean [" + bean +
"] has a target class with an MXBean interface but does not expose it in the proxy"); "] has a target class with an MXBean interface but does not expose it in the proxy");
} }
return new StandardMBean(bean, ifc, true); return new StandardMBean(bean, ((Class<Object>) ifc), true);
} }
else { else {
ifc = JmxUtils.getMBeanInterface(targetClass); ifc = JmxUtils.getMBeanInterface(targetClass);
if (ifc != null) { if (ifc != null) {
if (!(ifc.isInstance(bean))) { if (!ifc.isInstance(bean)) {
throw new NotCompliantMBeanException("Managed bean [" + bean + throw new NotCompliantMBeanException("Managed bean [" + bean +
"] has a target class with an MBean interface but does not expose it in the proxy"); "] has a target class with an MBean interface but does not expose it in the proxy");
} }
return new StandardMBean(bean, ifc); return new StandardMBean(bean, ((Class<Object>) ifc));
} }
} }
} }
@ -840,7 +841,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
*/ */
private void autodetectBeans(final AutodetectCapableMBeanInfoAssembler assembler) { private void autodetectBeans(final AutodetectCapableMBeanInfoAssembler assembler) {
autodetect(new AutodetectCallback() { autodetect(new AutodetectCallback() {
public boolean include(Class beanClass, String beanName) { public boolean include(Class<?> beanClass, String beanName) {
return assembler.includeBean(beanClass, beanName); return assembler.includeBean(beanClass, beanName);
} }
}); });
@ -852,7 +853,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
*/ */
private void autodetectMBeans() { private void autodetectMBeans() {
autodetect(new AutodetectCallback() { autodetect(new AutodetectCallback() {
public boolean include(Class beanClass, String beanName) { public boolean include(Class<?> beanClass, String beanName) {
return isMBean(beanClass); return isMBean(beanClass);
} }
}); });
@ -874,7 +875,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
for (String beanName : beanNames) { for (String beanName : beanNames) {
if (!isExcluded(beanName) && !isBeanDefinitionAbstract(this.beanFactory, beanName)) { if (!isExcluded(beanName) && !isBeanDefinitionAbstract(this.beanFactory, beanName)) {
try { try {
Class beanClass = this.beanFactory.getType(beanName); Class<?> beanClass = this.beanFactory.getType(beanName);
if (beanClass != null && callback.include(beanClass, beanName)) { if (beanClass != null && callback.include(beanClass, beanName)) {
boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName); boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName);
Object beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null); Object beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null);
@ -1060,7 +1061,7 @@ public class MBeanExporter extends MBeanRegistrationSupport
* @param beanClass the class of the bean * @param beanClass the class of the bean
* @param beanName the name of the bean * @param beanName the name of the bean
*/ */
boolean include(Class beanClass, String beanName); boolean include(Class<?> beanClass, String beanName);
} }

7
spring-context/src/test/java/org/springframework/jmx/AbstractMBeanServerTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import javax.management.ObjectName;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
@ -51,12 +52,14 @@ public abstract class AbstractMBeanServerTests {
protected MBeanServer server; protected MBeanServer server;
@Before @Before
public final void setUp() throws Exception { public final void setUp() throws Exception {
this.server = MBeanServerFactory.createMBeanServer(); this.server = MBeanServerFactory.createMBeanServer();
try { try {
onSetUp(); onSetUp();
} catch (Exception ex) { }
catch (Exception ex) {
releaseServer(); releaseServer();
throw ex; throw ex;
} }

57
spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.management.Attribute; import javax.management.Attribute;
import javax.management.InstanceNotFoundException; import javax.management.InstanceNotFoundException;
import javax.management.JMException; import javax.management.JMException;
@ -34,6 +33,7 @@ import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBeanInfo; import javax.management.modelmbean.ModelMBeanInfo;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@ -68,25 +68,10 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
private static final String OBJECT_NAME = "spring:test=jmxMBeanAdaptor"; private static final String OBJECT_NAME = "spring:test=jmxMBeanAdaptor";
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testRegisterNonNotificationListenerType() throws Exception {
Map listeners = new HashMap();
// put a non-NotificationListener instance in as a value...
listeners.put("*", this);
MBeanExporter exporter = new MBeanExporter();
try {
exporter.setNotificationListenerMappings(listeners);
fail("Must have thrown a ClassCastException when registering a non-NotificationListener instance as a NotificationListener.");
}
catch (ClassCastException expected) {
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test @Test
public void testRegisterNullNotificationListenerType() throws Exception { public void testRegisterNullNotificationListenerType() throws Exception {
Map listeners = new HashMap(); Map<String, NotificationListener> listeners = new HashMap<String, NotificationListener>();
// put null in as a value... // put null in as a value...
listeners.put("*", null); listeners.put("*", null);
MBeanExporter exporter = new MBeanExporter(); MBeanExporter exporter = new MBeanExporter();
@ -98,10 +83,9 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test @Test
public void testRegisterNotificationListenerForNonExistentMBean() throws Exception { public void testRegisterNotificationListenerForNonExistentMBean() throws Exception {
Map listeners = new HashMap(); Map<String, NotificationListener> listeners = new HashMap<String, NotificationListener>();
NotificationListener dummyListener = new NotificationListener() { NotificationListener dummyListener = new NotificationListener() {
@Override @Override
public void handleNotification(Notification notification, Object handback) { public void handleNotification(Notification notification, Object handback) {
@ -175,7 +159,8 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
assertNotNull(instance); assertNotNull(instance);
instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean3=true")); instance = server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean3=true"));
assertNotNull(instance); assertNotNull(instance);
} finally { }
finally {
bf.destroySingletons(); bf.destroySingletons();
} }
} }
@ -193,9 +178,11 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
try { try {
server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=false")); server.getObjectInstance(ObjectNameManager.getInstance("spring:mbean=false"));
fail("MBean with name spring:mbean=false should have been excluded"); fail("MBean with name spring:mbean=false should have been excluded");
} catch (InstanceNotFoundException expected) {
} }
} finally { catch (InstanceNotFoundException expected) {
}
}
finally {
bf.destroySingletons(); bf.destroySingletons();
} }
} }
@ -217,7 +204,8 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
assertNotNull(server.getObjectInstance(oname)); assertNotNull(server.getObjectInstance(oname));
name = (String) server.getAttribute(oname, "Name"); name = (String) server.getAttribute(oname, "Name");
assertEquals("Invalid name returned", "Juergen Hoeller", name); assertEquals("Invalid name returned", "Juergen Hoeller", name);
} finally { }
finally {
bf.destroySingletons(); bf.destroySingletons();
} }
} }
@ -228,7 +216,8 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource("autodetectNoMBeans.xml", getClass())); new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource("autodetectNoMBeans.xml", getClass()));
try { try {
bf.getBean("exporter"); bf.getBean("exporter");
} finally { }
finally {
bf.destroySingletons(); bf.destroySingletons();
} }
} }
@ -241,7 +230,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
MBeanExporter exporter = new MBeanExporter(); MBeanExporter exporter = new MBeanExporter();
exporter.setBeans(getBeanMap()); exporter.setBeans(getBeanMap());
exporter.setServer(server); exporter.setServer(server);
exporter.setListeners(new MBeanExporterListener[] { listener1, listener2 }); exporter.setListeners(listener1, listener2);
exporter.afterPropertiesSet(); exporter.afterPropertiesSet();
exporter.destroy(); exporter.destroy();
@ -257,7 +246,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
ProxyFactory factory = new ProxyFactory(); ProxyFactory factory = new ProxyFactory();
factory.setTarget(bean); factory.setTarget(bean);
factory.addAdvice(new NopInterceptor()); factory.addAdvice(new NopInterceptor());
factory.setInterfaces(new Class<?>[] { IJmxTestBean.class }); factory.setInterfaces(IJmxTestBean.class);
IJmxTestBean proxy = (IJmxTestBean) factory.getProxy(); IJmxTestBean proxy = (IJmxTestBean) factory.getProxy();
String name = "bean:mmm=whatever"; String name = "bean:mmm=whatever";
@ -377,8 +366,8 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
assertIsRegistered("Bean instance not registered", objectName); assertIsRegistered("Bean instance not registered", objectName);
Object result = server.invoke(objectName, "add", new Object[] { new Integer(2), new Integer(3) }, new String[] { Object result = server.invoke(objectName, "add", new Object[] {new Integer(2), new Integer(3)}, new String[] {
int.class.getName(), int.class.getName() }); int.class.getName(), int.class.getName()});
assertEquals("Incorrect result return from add", result, new Integer(5)); assertEquals("Incorrect result return from add", result, new Integer(5));
assertEquals("Incorrect attribute value", name, server.getAttribute(objectName, "Name")); assertEquals("Incorrect attribute value", name, server.getAttribute(objectName, "Name"));
@ -593,7 +582,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
exporter.setBeans(getBeanMap()); exporter.setBeans(getBeanMap());
exporter.setServer(this.server); exporter.setServer(this.server);
MockMBeanExporterListener listener = new MockMBeanExporterListener(); MockMBeanExporterListener listener = new MockMBeanExporterListener();
exporter.setListeners(new MBeanExporterListener[] { listener }); exporter.setListeners(listener);
exporter.afterPropertiesSet(); exporter.afterPropertiesSet();
assertIsRegistered("The bean was not registered with the MBeanServer", assertIsRegistered("The bean was not registered with the MBeanServer",
ObjectNameManager.getInstance(OBJECT_NAME)); ObjectNameManager.getInstance(OBJECT_NAME));
@ -702,6 +691,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
assertEquals("Incorrect ObjectName in unregister", desired, listener.getUnregistered().get(0)); assertEquals("Incorrect ObjectName in unregister", desired, listener.getUnregistered().get(0));
} }
private static class InvokeDetectAssembler implements MBeanInfoAssembler { private static class InvokeDetectAssembler implements MBeanInfoAssembler {
private boolean invoked = false; private boolean invoked = false;
@ -713,6 +703,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
private static class MockMBeanExporterListener implements MBeanExporterListener { private static class MockMBeanExporterListener implements MBeanExporterListener {
private List<ObjectName> registered = new ArrayList<ObjectName>(); private List<ObjectName> registered = new ArrayList<ObjectName>();
@ -738,6 +729,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
private static class SelfNamingTestBean implements SelfNaming { private static class SelfNamingTestBean implements SelfNaming {
private ObjectName objectName; private ObjectName objectName;
@ -752,11 +744,13 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
public static interface PersonMBean { public static interface PersonMBean {
String getName(); String getName();
} }
public static class Person implements PersonMBean { public static class Person implements PersonMBean {
private String name; private String name;
@ -771,6 +765,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
public static final class StubNotificationListener implements NotificationListener { public static final class StubNotificationListener implements NotificationListener {
private List<Notification> notifications = new ArrayList<Notification>(); private List<Notification> notifications = new ArrayList<Notification>();
@ -785,6 +780,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
private static class RuntimeExceptionThrowingConstructorBean { private static class RuntimeExceptionThrowingConstructorBean {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -793,6 +789,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
} }
} }
private static final class NamedBeanAutodetectCapableMBeanInfoAssemblerStub extends private static final class NamedBeanAutodetectCapableMBeanInfoAssemblerStub extends
SimpleReflectiveMBeanInfoAssembler implements AutodetectCapableMBeanInfoAssembler { SimpleReflectiveMBeanInfoAssembler implements AutodetectCapableMBeanInfoAssembler {

Loading…
Cancel
Save