Introduce @EnableMBeanExport

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-8943
This commit is contained in:
Phillip Webb
2012-10-22 19:25:57 -07:00
committed by Chris Beams
parent 6179261d58
commit cae08db6a9
11 changed files with 578 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -24,7 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.jmx.export.annotation.AnnotationMBeanExporter;
import org.springframework.jmx.support.MBeanRegistrationSupport;
import org.springframework.jmx.support.RegistrationPolicy;
import org.springframework.util.StringUtils;
/**
@@ -84,14 +84,14 @@ class MBeanExportBeanDefinitionParser extends AbstractBeanDefinitionParser {
}
String registration = element.getAttribute(REGISTRATION_ATTRIBUTE);
int registrationBehavior = MBeanRegistrationSupport.REGISTRATION_FAIL_ON_EXISTING;
RegistrationPolicy registrationPolicy = RegistrationPolicy.FAIL_ON_EXISTING;
if (REGISTRATION_IGNORE_EXISTING.equals(registration)) {
registrationBehavior = MBeanRegistrationSupport.REGISTRATION_IGNORE_EXISTING;
registrationPolicy = RegistrationPolicy.IGNORE_EXISTING;
}
else if (REGISTRATION_REPLACE_EXISTING.equals(registration)) {
registrationBehavior = MBeanRegistrationSupport.REGISTRATION_REPLACE_EXISTING;
registrationPolicy = RegistrationPolicy.REPLACE_EXISTING;
}
builder.addPropertyValue("registrationBehavior", registrationBehavior);
builder.addPropertyValue("registrationPolicy", registrationPolicy);
return builder.getBeanDefinition();
}

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -29,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Constants;
import org.springframework.util.Assert;
/**
* Provides supporting infrastructure for registering MBeans with an
@@ -47,21 +48,22 @@ import org.springframework.core.Constants;
* register an MBean using a {@link javax.management.ObjectName} that is
* already used.
*
* <p>By setting the {@link #setRegistrationBehaviorName(String) registrationBehaviorName}
* property to <code>REGISTRATION_IGNORE_EXISTING</code> the registration process
* <p>By setting the {@link #setRegistrationPolicy(RegistrationPolicy) registrationPolicy}
* property to {@link RegistrationPolicy#IGNORE_EXISTING} the registration process
* will simply ignore existing MBeans leaving them registered. This is useful in settings
* where multiple applications want to share a common MBean in a shared {@link MBeanServer}.
*
* <p>Setting {@link #setRegistrationBehaviorName(String) registrationBehaviorName} property
* to <code>REGISTRATION_REPLACE_EXISTING</code> will cause existing MBeans to be replaced
* <p>Setting {@link #setRegistrationPolicy(RegistrationPolicy) registrationPolicy} property
* to {@link RegistrationPolicy#REPLACE_EXISTING} will cause existing MBeans to be replaced
* during registration if necessary. This is useful in situations where you can't guarantee
* the state of your {@link MBeanServer}.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Phillip Webb
* @since 2.0
* @see #setServer
* @see #setRegistrationBehaviorName
* @see #setRegistrationPolicy
* @see org.springframework.jmx.export.MBeanExporter
*/
public class MBeanRegistrationSupport {
@@ -70,19 +72,25 @@ public class MBeanRegistrationSupport {
* Constant indicating that registration should fail when
* attempting to register an MBean under a name that already exists.
* <p>This is the default registration behavior.
* @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#FAIL_ON_EXISTING}
*/
@Deprecated
public static final int REGISTRATION_FAIL_ON_EXISTING = 0;
/**
* Constant indicating that registration should ignore the affected MBean
* when attempting to register an MBean under a name that already exists.
* @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#IGNORE_EXISTING}
*/
@Deprecated
public static final int REGISTRATION_IGNORE_EXISTING = 1;
/**
* Constant indicating that registration should replace the affected MBean
* when attempting to register an MBean under a name that already exists.
* @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#REPLACE_EXISTING}
*/
@Deprecated
public static final int REGISTRATION_REPLACE_EXISTING = 2;
@@ -107,10 +115,10 @@ public class MBeanRegistrationSupport {
protected final Set<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>();
/**
* The action take when registering an MBean and finding that it already exists.
* The policy used when registering an MBean and finding that it already exists.
* By default an exception is raised.
*/
private int registrationBehavior = REGISTRATION_FAIL_ON_EXISTING;
private RegistrationPolicy registrationPolicy = RegistrationPolicy.FAIL_ON_EXISTING;
/**
@@ -136,22 +144,37 @@ public class MBeanRegistrationSupport {
* @see #REGISTRATION_FAIL_ON_EXISTING
* @see #REGISTRATION_IGNORE_EXISTING
* @see #REGISTRATION_REPLACE_EXISTING
* @deprecated since Spring 3.2, in favor of {@link #setRegistrationPolicy(RegistrationPolicy)}
*/
@Deprecated
public void setRegistrationBehaviorName(String registrationBehavior) {
setRegistrationBehavior(constants.asNumber(registrationBehavior).intValue());
}
/**
* Specify what action should be taken when attempting to register an MBean
* Specify what action should be taken when attempting to register an MBean
* under an {@link javax.management.ObjectName} that already exists.
* <p>Default is REGISTRATION_FAIL_ON_EXISTING.
* @see #setRegistrationBehaviorName(String)
* @see #REGISTRATION_FAIL_ON_EXISTING
* @see #REGISTRATION_IGNORE_EXISTING
* @see #REGISTRATION_REPLACE_EXISTING
* @deprecated since Spring 3.2, in favor of {@link #setRegistrationPolicy(RegistrationPolicy)}
*/
@Deprecated
public void setRegistrationBehavior(int registrationBehavior) {
this.registrationBehavior = registrationBehavior;
setRegistrationPolicy(RegistrationPolicy.valueOf(registrationBehavior));
}
/**
* The policy to use when attempting to register an MBean
* under an {@link javax.management.ObjectName} that already exists.
* @param registrationPolicy the policy to use
* @since 3.2
*/
public void setRegistrationPolicy(RegistrationPolicy registrationPolicy) {
Assert.notNull(registrationPolicy, "RegistrationPolicy must not be null");
this.registrationPolicy = registrationPolicy;
}
@@ -169,12 +192,12 @@ public class MBeanRegistrationSupport {
registeredBean = this.server.registerMBean(mbean, objectName);
}
catch (InstanceAlreadyExistsException ex) {
if (this.registrationBehavior == REGISTRATION_IGNORE_EXISTING) {
if (this.registrationPolicy == RegistrationPolicy.IGNORE_EXISTING) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring existing MBean at [" + objectName + "]");
}
}
else if (this.registrationBehavior == REGISTRATION_REPLACE_EXISTING) {
else if (this.registrationPolicy == RegistrationPolicy.REPLACE_EXISTING) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Replacing existing MBean at [" + objectName + "]");

View File

@@ -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);
}
}