Browse Source
Add support for @EnableMBeanExport annotation allowing @Configuration classes to easily export all MBeans and @ManagedResources from the Spring application context. The annotation is functionally equivalent to the XML <context:mbean-export/> element. Issue: SPR-8943pull/174/merge
11 changed files with 578 additions and 32 deletions
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.jmx.export.annotation; |
||||
|
||||
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; |
||||
|
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.jmx.support.RegistrationPolicy; |
||||
|
||||
/** |
||||
* Enables default exporting of all standard {@code MBean}s from the Spring context, as |
||||
* well as well all {@code @ManagedResource} annotated beans. |
||||
* |
||||
* <p>The resulting {@link org.springframework.jmx.export.MBeanExporter MBeanExporter} |
||||
* bean is defined under the name "mbeanExporter". Alternatively, consider defining a |
||||
* custom {@link AnnotationMBeanExporter} bean explicitly. |
||||
* |
||||
* <p>This annotation is modeled after and functionally equivalent to Spring XML's |
||||
* {@code <context:mbean-export/>} element. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 3.2 |
||||
* @see MBeanExportConfiguration |
||||
*/ |
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Import(MBeanExportConfiguration.class) |
||||
public @interface EnableMBeanExport { |
||||
|
||||
/** |
||||
* The default domain to use when generating JMX ObjectNames. |
||||
*/ |
||||
String defaultDomain() default ""; |
||||
|
||||
/** |
||||
* The bean name of the MBeanServer to which MBeans should be exported. Default is to |
||||
* use the platform's default MBeanServer. |
||||
*/ |
||||
String server() default ""; |
||||
|
||||
/** |
||||
* The policy to use when attempting to register an MBean under an |
||||
* {@link javax.management.ObjectName} that already exists. Defaults to |
||||
* {@link RegistrationPolicy#FAIL_ON_EXISTING}. |
||||
*/ |
||||
RegistrationPolicy registration() default RegistrationPolicy.FAIL_ON_EXISTING; |
||||
} |
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.jmx.export.annotation; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import javax.management.MBeanServer; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.BeanFactory; |
||||
import org.springframework.beans.factory.BeanFactoryAware; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.ImportAware; |
||||
import org.springframework.context.annotation.Role; |
||||
import org.springframework.core.annotation.AnnotationAttributes; |
||||
import org.springframework.core.type.AnnotationMetadata; |
||||
import org.springframework.jmx.support.RegistrationPolicy; |
||||
import org.springframework.jmx.support.WebSphereMBeanServerFactoryBean; |
||||
import org.springframework.jndi.JndiObjectFactoryBean; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* {@code @Configuration} class that registers a {@link AnnotationMBeanExporter} bean. |
||||
* |
||||
* <p>This configuration class is automatically imported when using the @{@link |
||||
* EnableMBeanExport} annotation. See its Javadoc for complete usage details. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Chris Beams |
||||
* @since 3.2 |
||||
* @see EnableMBeanExport |
||||
*/ |
||||
@Configuration |
||||
public class MBeanExportConfiguration implements ImportAware, BeanFactoryAware { |
||||
|
||||
private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter"; |
||||
|
||||
private AnnotationAttributes attributes; |
||||
|
||||
private BeanFactory beanFactory; |
||||
|
||||
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) { |
||||
Map<String, Object> map = importMetadata.getAnnotationAttributes(EnableMBeanExport.class.getName()); |
||||
this.attributes = AnnotationAttributes.fromMap(map); |
||||
Assert.notNull(this.attributes, "@EnableMBeanExport is not present on " + |
||||
"importing class " + importMetadata.getClassName()); |
||||
} |
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { |
||||
this.beanFactory = beanFactory; |
||||
} |
||||
|
||||
@Bean(name=MBEAN_EXPORTER_BEAN_NAME) |
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
||||
public AnnotationMBeanExporter mbeanExporter() { |
||||
AnnotationMBeanExporter exporter = new AnnotationMBeanExporter(); |
||||
setupDomain(exporter); |
||||
setupServer(exporter); |
||||
setupRegistrationPolicy(exporter); |
||||
return exporter; |
||||
} |
||||
|
||||
private void setupDomain(AnnotationMBeanExporter exporter) { |
||||
String defaultDomain = this.attributes.getString("defaultDomain"); |
||||
if (StringUtils.hasText(defaultDomain)) { |
||||
exporter.setDefaultDomain(defaultDomain); |
||||
} |
||||
} |
||||
|
||||
private void setupServer(AnnotationMBeanExporter exporter) { |
||||
String server = this.attributes.getString("server"); |
||||
if (StringUtils.hasText(server)) { |
||||
exporter.setServer(this.beanFactory.getBean(server, MBeanServer.class)); |
||||
} |
||||
else { |
||||
SpecificPlatform specificPlatform = SpecificPlatform.get(); |
||||
if(specificPlatform != null) { |
||||
exporter.setServer(specificPlatform.getMBeanServer()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void setupRegistrationPolicy(AnnotationMBeanExporter exporter) { |
||||
RegistrationPolicy registrationPolicy = this.attributes.getEnum("registration"); |
||||
exporter.setRegistrationPolicy(registrationPolicy); |
||||
} |
||||
|
||||
|
||||
private static enum SpecificPlatform { |
||||
|
||||
WEBLOGIC("weblogic.management.Helper") { |
||||
@Override |
||||
public FactoryBean<?> getMBeanServerFactory() { |
||||
JndiObjectFactoryBean factory = new JndiObjectFactoryBean(); |
||||
factory.setJndiName("java:comp/env/jmx/runtime"); |
||||
return factory; |
||||
} |
||||
}, |
||||
|
||||
WEBSPHERE("com.ibm.websphere.management.AdminServiceFactory") { |
||||
@Override |
||||
public FactoryBean<MBeanServer> getMBeanServerFactory() { |
||||
return new WebSphereMBeanServerFactoryBean(); |
||||
} |
||||
}; |
||||
|
||||
private final String identifyingClass; |
||||
|
||||
private SpecificPlatform(String identifyingClass) { |
||||
this.identifyingClass = identifyingClass; |
||||
} |
||||
|
||||
public MBeanServer getMBeanServer() { |
||||
Object server; |
||||
try { |
||||
server = getMBeanServerFactory().getObject(); |
||||
Assert.isInstanceOf(MBeanServer.class, server); |
||||
return (MBeanServer) server; |
||||
} catch (Exception ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
protected abstract FactoryBean<?> getMBeanServerFactory(); |
||||
|
||||
public static SpecificPlatform get() { |
||||
ClassLoader classLoader = MBeanExportConfiguration.class.getClassLoader(); |
||||
for (SpecificPlatform environment : values()) { |
||||
if(ClassUtils.isPresent(environment.identifyingClass, classLoader)) { |
||||
return environment; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.jmx.support; |
||||
|
||||
/** |
||||
* Indicates registration behavior when attempting to register an MBean that already |
||||
* exists. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Chris Beams |
||||
* @since 3.2 |
||||
*/ |
||||
public enum RegistrationPolicy { |
||||
|
||||
/** |
||||
* Registration should fail when attempting to register an MBean under a name that |
||||
* already exists. |
||||
*/ |
||||
FAIL_ON_EXISTING, |
||||
|
||||
/** |
||||
* Registration should ignore the affected MBean when attempting to register an MBean |
||||
* under a name that already exists. |
||||
*/ |
||||
IGNORE_EXISTING, |
||||
|
||||
/** |
||||
* Registration should replace the affected MBean when attempting to register an MBean |
||||
* under a name that already exists. |
||||
*/ |
||||
REPLACE_EXISTING; |
||||
|
||||
/** |
||||
* Translate from an {@link MBeanRegistrationSupport} registration behavior constant |
||||
* to a {@link RegistrationPolicy} enum value. |
||||
* @param registrationBehavior one of the now-deprecated REGISTRATION_* constants |
||||
* available in {@link MBeanRegistrationSupport}. |
||||
*/ |
||||
@SuppressWarnings("deprecation") |
||||
public static RegistrationPolicy valueOf(int registrationBehavior) { |
||||
switch (registrationBehavior) { |
||||
case MBeanRegistrationSupport.REGISTRATION_IGNORE_EXISTING: |
||||
return RegistrationPolicy.IGNORE_EXISTING; |
||||
case MBeanRegistrationSupport.REGISTRATION_REPLACE_EXISTING: |
||||
return RegistrationPolicy.REPLACE_EXISTING; |
||||
case MBeanRegistrationSupport.REGISTRATION_FAIL_ON_EXISTING: |
||||
return RegistrationPolicy.FAIL_ON_EXISTING; |
||||
} |
||||
throw new IllegalArgumentException( |
||||
"Unknown MBean registration behavior: " + registrationBehavior); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,182 @@
@@ -0,0 +1,182 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.jmx.export.annotation; |
||||
|
||||
import javax.management.MBeanServer; |
||||
import javax.management.ObjectName; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.ComponentScan; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Lazy; |
||||
import org.springframework.jmx.export.MBeanExporterTests; |
||||
import org.springframework.jmx.export.TestDynamicMBean; |
||||
import org.springframework.jmx.support.MBeanServerFactoryBean; |
||||
import org.springframework.jmx.support.ObjectNameManager; |
||||
import org.springframework.jmx.support.RegistrationPolicy; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Tests for {@link EnableMBeanExport} and {@link MBeanExportConfiguration}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @see AnnotationLazyInitMBeanTests |
||||
*/ |
||||
public class EnableMBeanExportConfigurationTests { |
||||
|
||||
@Test |
||||
public void testLazyNaming() throws Exception { |
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( |
||||
LazyNamingConfiguration.class); |
||||
try { |
||||
MBeanServer server = (MBeanServer) ctx.getBean("server"); |
||||
ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
String name = (String) server.getAttribute(oname, "Name"); |
||||
assertEquals("Invalid name returned", "TEST", name); |
||||
} |
||||
finally { |
||||
ctx.close(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testLazyAssembling() throws Exception { |
||||
AnnotationConfigApplicationContext ctx = |
||||
new AnnotationConfigApplicationContext(LazyAssemblingConfiguration.class); |
||||
try { |
||||
MBeanServer server = (MBeanServer) ctx.getBean("server"); |
||||
|
||||
ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
String name = (String) server.getAttribute(oname, "Name"); |
||||
assertEquals("Invalid name returned", "TEST", name); |
||||
|
||||
oname = ObjectNameManager.getInstance("bean:name=testBean5"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
name = (String) server.getAttribute(oname, "Name"); |
||||
assertEquals("Invalid name returned", "FACTORY", name); |
||||
|
||||
oname = ObjectNameManager.getInstance("spring:mbean=true"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
name = (String) server.getAttribute(oname, "Name"); |
||||
assertEquals("Invalid name returned", "Rob Harrop", name); |
||||
|
||||
oname = ObjectNameManager.getInstance("spring:mbean=another"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
name = (String) server.getAttribute(oname, "Name"); |
||||
assertEquals("Invalid name returned", "Juergen Hoeller", name); |
||||
} |
||||
finally { |
||||
ctx.close(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testComponentScan() throws Exception { |
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( |
||||
ComponentScanConfiguration.class); |
||||
try { |
||||
MBeanServer server = (MBeanServer) ctx.getBean("server"); |
||||
ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4"); |
||||
assertNotNull(server.getObjectInstance(oname)); |
||||
String name = (String) server.getAttribute(oname, "Name"); |
||||
assertNull(name); |
||||
} finally { |
||||
ctx.close(); |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
@EnableMBeanExport(server = "server") |
||||
static class LazyNamingConfiguration { |
||||
|
||||
@Bean |
||||
public MBeanServerFactoryBean server() throws Exception { |
||||
return new MBeanServerFactoryBean(); |
||||
} |
||||
|
||||
@Bean |
||||
@Lazy |
||||
public AnnotationTestBean testBean() { |
||||
AnnotationTestBean bean = new AnnotationTestBean(); |
||||
bean.setName("TEST"); |
||||
bean.setAge(100); |
||||
return bean; |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
@EnableMBeanExport(server="server", registration=RegistrationPolicy.REPLACE_EXISTING) |
||||
static class LazyAssemblingConfiguration { |
||||
|
||||
@Bean |
||||
public MBeanServerFactoryBean server() throws Exception { |
||||
return new MBeanServerFactoryBean(); |
||||
} |
||||
|
||||
@Bean(name="bean:name=testBean4") |
||||
@Lazy |
||||
public AnnotationTestBean testBean4() { |
||||
AnnotationTestBean bean = new AnnotationTestBean(); |
||||
bean.setName("TEST"); |
||||
bean.setAge(100); |
||||
return bean; |
||||
} |
||||
|
||||
@Bean(name="bean:name=testBean5") |
||||
public AnnotationTestBeanFactory testBean5() throws Exception { |
||||
return new AnnotationTestBeanFactory(); |
||||
} |
||||
|
||||
@Bean(name="spring:mbean=true") |
||||
@Lazy |
||||
public TestDynamicMBean dynamic() { |
||||
return new TestDynamicMBean(); |
||||
} |
||||
|
||||
@Bean(name="spring:mbean=another") |
||||
@Lazy |
||||
public MBeanExporterTests.Person person() { |
||||
MBeanExporterTests.Person person = new MBeanExporterTests.Person(); |
||||
person.setName("Juergen Hoeller"); |
||||
return person; |
||||
} |
||||
|
||||
@Bean |
||||
@Lazy |
||||
public Object notLoadable() throws Exception { |
||||
return Class.forName("does.not.exist").newInstance(); |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
@ComponentScan(excludeFilters = @ComponentScan.Filter(value=Configuration.class)) |
||||
@EnableMBeanExport(server = "server") |
||||
static class ComponentScanConfiguration { |
||||
|
||||
@Bean |
||||
public MBeanServerFactoryBean server() throws Exception { |
||||
return new MBeanServerFactoryBean(); |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,14 +1,15 @@
@@ -1,14 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<beans xmlns="http://www.springframework.org/schema/beans" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns:context="http://www.springframework.org/schema/context" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" |
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd |
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> |
||||
|
||||
<context:mbean-export server="server"/> |
||||
<context:mbean-export server="server" /> |
||||
|
||||
<bean id="server" class="org.springframework.jmx.support.MBeanServerFactoryBean"/> |
||||
<bean id="server" class="org.springframework.jmx.support.MBeanServerFactoryBean" /> |
||||
|
||||
<context:component-scan base-package="org.springframework.jmx.export.annotation"/> |
||||
<context:component-scan base-package="org.springframework.jmx.export.annotation"> |
||||
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration" /> |
||||
</context:component-scan> |
||||
|
||||
</beans> |
||||
|
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.jmx.support; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import static org.hamcrest.CoreMatchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Unit tests for {@link RegistrationPolicy}. |
||||
* |
||||
* @author Chris Beams |
||||
*/ |
||||
public class RegistrationPolicyTests { |
||||
|
||||
@Test |
||||
@SuppressWarnings("deprecation") |
||||
public void convertRegistrationBehaviorToRegistrationPolicy() { |
||||
assertThat( |
||||
RegistrationPolicy.valueOf(MBeanRegistrationSupport.REGISTRATION_FAIL_ON_EXISTING), |
||||
equalTo(RegistrationPolicy.FAIL_ON_EXISTING)); |
||||
assertThat( |
||||
RegistrationPolicy.valueOf(MBeanRegistrationSupport.REGISTRATION_IGNORE_EXISTING), |
||||
equalTo(RegistrationPolicy.IGNORE_EXISTING)); |
||||
assertThat( |
||||
RegistrationPolicy.valueOf(MBeanRegistrationSupport.REGISTRATION_REPLACE_EXISTING), |
||||
equalTo(RegistrationPolicy.REPLACE_EXISTING)); |
||||
|
||||
try { |
||||
RegistrationPolicy.valueOf(Integer.MAX_VALUE); |
||||
fail("Expected IllegalArgumentException"); |
||||
} |
||||
catch (IllegalArgumentException ex) { |
||||
assertTrue(ex.getMessage().startsWith("Unknown MBean registration behavior")); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue