/*
 * Copyright 2002-2018 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 java.beans.PropertyDescriptor;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.List;
import javax.management.DynamicMBean;
import javax.management.JMX;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.jmx.MBeanServerNotFoundException;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

Collection of generic utility methods to support Spring JMX. Includes a convenient method to locate an MBeanServer.
Author:Rob Harrop, Juergen Hoeller
See Also:
Since:1.2
/** * Collection of generic utility methods to support Spring JMX. * Includes a convenient method to locate an MBeanServer. * * @author Rob Harrop * @author Juergen Hoeller * @since 1.2 * @see #locateMBeanServer */
public abstract class JmxUtils {
The key used when extending an existing ObjectName with the identity hash code of its corresponding managed resource.
/** * The key used when extending an existing {@link ObjectName} with the * identity hash code of its corresponding managed resource. */
public static final String IDENTITY_OBJECT_NAME_KEY = "identity";
Suffix used to identify an MBean interface.
/** * Suffix used to identify an MBean interface. */
private static final String MBEAN_SUFFIX = "MBean"; private static final Log logger = LogFactory.getLog(JmxUtils.class);
Attempt to find a locally running MBeanServer. Fails if no MBeanServer can be found. Logs a warning if more than one MBeanServer found, returning the first one from the list.
Throws:
See Also:
Returns:the MBeanServer if found
/** * Attempt to find a locally running {@code MBeanServer}. Fails if no * {@code MBeanServer} can be found. Logs a warning if more than one * {@code MBeanServer} found, returning the first one from the list. * @return the {@code MBeanServer} if found * @throws MBeanServerNotFoundException if no {@code MBeanServer} could be found * @see javax.management.MBeanServerFactory#findMBeanServer */
public static MBeanServer locateMBeanServer() throws MBeanServerNotFoundException { return locateMBeanServer(null); }
Attempt to find a locally running MBeanServer. Fails if no MBeanServer can be found. Logs a warning if more than one MBeanServer found, returning the first one from the list.
Params:
  • agentId – the agent identifier of the MBeanServer to retrieve. If this parameter is null, all registered MBeanServers are considered. If the empty String is given, the platform MBeanServer will be returned.
Throws:
See Also:
Returns:the MBeanServer if found
/** * Attempt to find a locally running {@code MBeanServer}. Fails if no * {@code MBeanServer} can be found. Logs a warning if more than one * {@code MBeanServer} found, returning the first one from the list. * @param agentId the agent identifier of the MBeanServer to retrieve. * If this parameter is {@code null}, all registered MBeanServers are considered. * If the empty String is given, the platform MBeanServer will be returned. * @return the {@code MBeanServer} if found * @throws MBeanServerNotFoundException if no {@code MBeanServer} could be found * @see javax.management.MBeanServerFactory#findMBeanServer(String) */
public static MBeanServer locateMBeanServer(@Nullable String agentId) throws MBeanServerNotFoundException { MBeanServer server = null; // null means any registered server, but "" specifically means the platform server if (!"".equals(agentId)) { List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(agentId); if (!CollectionUtils.isEmpty(servers)) { // Check to see if an MBeanServer is registered. if (servers.size() > 1 && logger.isInfoEnabled()) { logger.info("Found more than one MBeanServer instance" + (agentId != null ? " with agent id [" + agentId + "]" : "") + ". Returning first from list."); } server = servers.get(0); } } if (server == null && !StringUtils.hasLength(agentId)) { // Attempt to load the PlatformMBeanServer. try { server = ManagementFactory.getPlatformMBeanServer(); } catch (SecurityException ex) { throw new MBeanServerNotFoundException("No specific MBeanServer found, " + "and not allowed to obtain the Java platform MBeanServer", ex); } } if (server == null) { throw new MBeanServerNotFoundException( "Unable to locate an MBeanServer instance" + (agentId != null ? " with agent id [" + agentId + "]" : "")); } if (logger.isDebugEnabled()) { logger.debug("Found MBeanServer: " + server); } return server; }
Convert an array of MBeanParameterInfo into an array of Class instances corresponding to the parameters.
Params:
  • paramInfo – the JMX parameter info
Throws:
Returns:the parameter types as classes
/** * Convert an array of {@code MBeanParameterInfo} into an array of * {@code Class} instances corresponding to the parameters. * @param paramInfo the JMX parameter info * @return the parameter types as classes * @throws ClassNotFoundException if a parameter type could not be resolved */
@Nullable public static Class<?>[] parameterInfoToTypes(@Nullable MBeanParameterInfo[] paramInfo) throws ClassNotFoundException { return parameterInfoToTypes(paramInfo, ClassUtils.getDefaultClassLoader()); }
Convert an array of MBeanParameterInfo into an array of Class instances corresponding to the parameters.
Params:
  • paramInfo – the JMX parameter info
  • classLoader – the ClassLoader to use for loading parameter types
Throws:
Returns:the parameter types as classes
/** * Convert an array of {@code MBeanParameterInfo} into an array of * {@code Class} instances corresponding to the parameters. * @param paramInfo the JMX parameter info * @param classLoader the ClassLoader to use for loading parameter types * @return the parameter types as classes * @throws ClassNotFoundException if a parameter type could not be resolved */
@Nullable public static Class<?>[] parameterInfoToTypes( @Nullable MBeanParameterInfo[] paramInfo, @Nullable ClassLoader classLoader) throws ClassNotFoundException { Class<?>[] types = null; if (paramInfo != null && paramInfo.length > 0) { types = new Class<?>[paramInfo.length]; for (int x = 0; x < paramInfo.length; x++) { types[x] = ClassUtils.forName(paramInfo[x].getType(), classLoader); } } return types; }
Create a String[] representing the argument signature of a method. Each element in the array is the fully qualified class name of the corresponding argument in the methods signature.
Params:
  • method – the method to build an argument signature for
Returns:the signature as array of argument types
/** * Create a {@code String[]} representing the argument signature of a * method. Each element in the array is the fully qualified class name * of the corresponding argument in the methods signature. * @param method the method to build an argument signature for * @return the signature as array of argument types */
public static String[] getMethodSignature(Method method) { Class<?>[] types = method.getParameterTypes(); String[] signature = new String[types.length]; for (int x = 0; x < types.length; x++) { signature[x] = types[x].getName(); } return signature; }
Return the JMX attribute name to use for the given JavaBeans property.

When using strict casing, a JavaBean property with a getter method such as getFoo() translates to an attribute called Foo. With strict casing disabled, getFoo() would translate to just foo.

Params:
  • property – the JavaBeans property descriptor
  • useStrictCasing – whether to use strict casing
Returns:the JMX attribute name to use
/** * Return the JMX attribute name to use for the given JavaBeans property. * <p>When using strict casing, a JavaBean property with a getter method * such as {@code getFoo()} translates to an attribute called * {@code Foo}. With strict casing disabled, {@code getFoo()} * would translate to just {@code foo}. * @param property the JavaBeans property descriptor * @param useStrictCasing whether to use strict casing * @return the JMX attribute name to use */
public static String getAttributeName(PropertyDescriptor property, boolean useStrictCasing) { if (useStrictCasing) { return StringUtils.capitalize(property.getName()); } else { return property.getName(); } }
Append an additional key/value pair to an existing ObjectName with the key being the static value identity and the value being the identity hash code of the managed resource being exposed on the supplied ObjectName. This can be used to provide a unique ObjectName for each distinct instance of a particular bean or class. Useful when generating ObjectNames at runtime for a set of managed resources based on the template value supplied by a ObjectNamingStrategy.
Params:
  • objectName – the original JMX ObjectName
  • managedResource – the MBean instance
Throws:
See Also:
Returns:an ObjectName with the MBean identity added
/** * Append an additional key/value pair to an existing {@link ObjectName} with the key being * the static value {@code identity} and the value being the identity hash code of the * managed resource being exposed on the supplied {@link ObjectName}. This can be used to * provide a unique {@link ObjectName} for each distinct instance of a particular bean or * class. Useful when generating {@link ObjectName ObjectNames} at runtime for a set of * managed resources based on the template value supplied by a * {@link org.springframework.jmx.export.naming.ObjectNamingStrategy}. * @param objectName the original JMX ObjectName * @param managedResource the MBean instance * @return an ObjectName with the MBean identity added * @throws MalformedObjectNameException in case of an invalid object name specification * @see org.springframework.util.ObjectUtils#getIdentityHexString(Object) */
public static ObjectName appendIdentityToObjectName(ObjectName objectName, Object managedResource) throws MalformedObjectNameException { Hashtable<String, String> keyProperties = objectName.getKeyPropertyList(); keyProperties.put(IDENTITY_OBJECT_NAME_KEY, ObjectUtils.getIdentityHexString(managedResource)); return ObjectNameManager.getInstance(objectName.getDomain(), keyProperties); }
Return the class or interface to expose for the given bean. This is the class that will be searched for attributes and operations (for example, checked for annotations).

This implementation returns the superclass for a CGLIB proxy and the class of the given bean else (for a JDK proxy or a plain bean class).

Params:
  • managedBean – the bean instance (might be an AOP proxy)
See Also:
Returns:the bean class to expose
/** * Return the class or interface to expose for the given bean. * This is the class that will be searched for attributes and operations * (for example, checked for annotations). * <p>This implementation returns the superclass for a CGLIB proxy and * the class of the given bean else (for a JDK proxy or a plain bean class). * @param managedBean the bean instance (might be an AOP proxy) * @return the bean class to expose * @see org.springframework.util.ClassUtils#getUserClass(Object) */
public static Class<?> getClassToExpose(Object managedBean) { return ClassUtils.getUserClass(managedBean); }
Return the class or interface to expose for the given bean class. This is the class that will be searched for attributes and operations (for example, checked for annotations).

This implementation returns the superclass for a CGLIB proxy and the class of the given bean else (for a JDK proxy or a plain bean class).

Params:
  • clazz – the bean class (might be an AOP proxy class)
See Also:
Returns:the bean class to expose
/** * Return the class or interface to expose for the given bean class. * This is the class that will be searched for attributes and operations * (for example, checked for annotations). * <p>This implementation returns the superclass for a CGLIB proxy and * the class of the given bean else (for a JDK proxy or a plain bean class). * @param clazz the bean class (might be an AOP proxy class) * @return the bean class to expose * @see org.springframework.util.ClassUtils#getUserClass(Class) */
public static Class<?> getClassToExpose(Class<?> clazz) { return ClassUtils.getUserClass(clazz); }
Determine whether the given bean class qualifies as an MBean as-is.

This implementation checks for DynamicMBean classes as well as classes with corresponding "*MBean" interface (Standard MBeans) or corresponding "*MXBean" interface (Java 6 MXBeans).

Params:
  • clazz – the bean class to analyze
See Also:
Returns:whether the class qualifies as an MBean
/** * Determine whether the given bean class qualifies as an MBean as-is. * <p>This implementation checks for {@link javax.management.DynamicMBean} * classes as well as classes with corresponding "*MBean" interface * (Standard MBeans) or corresponding "*MXBean" interface (Java 6 MXBeans). * @param clazz the bean class to analyze * @return whether the class qualifies as an MBean * @see org.springframework.jmx.export.MBeanExporter#isMBean(Class) */
public static boolean isMBean(@Nullable Class<?> clazz) { return (clazz != null && (DynamicMBean.class.isAssignableFrom(clazz) || (getMBeanInterface(clazz) != null || getMXBeanInterface(clazz) != null))); }
Return the Standard MBean interface for the given class, if any (that is, an interface whose name matches the class name of the given class but with suffix "MBean").
Params:
  • clazz – the class to check
Returns:the Standard MBean interface for the given class
/** * Return the Standard MBean interface for the given class, if any * (that is, an interface whose name matches the class name of the * given class but with suffix "MBean"). * @param clazz the class to check * @return the Standard MBean interface for the given class */
@Nullable public static Class<?> getMBeanInterface(@Nullable Class<?> clazz) { if (clazz == null || clazz.getSuperclass() == null) { return null; } String mbeanInterfaceName = clazz.getName() + MBEAN_SUFFIX; Class<?>[] implementedInterfaces = clazz.getInterfaces(); for (Class<?> iface : implementedInterfaces) { if (iface.getName().equals(mbeanInterfaceName)) { return iface; } } return getMBeanInterface(clazz.getSuperclass()); }
Return the Java 6 MXBean interface exists for the given class, if any (that is, an interface whose name ends with "MXBean" and/or carries an appropriate MXBean annotation).
Params:
  • clazz – the class to check
Returns:whether there is an MXBean interface for the given class
/** * Return the Java 6 MXBean interface exists for the given class, if any * (that is, an interface whose name ends with "MXBean" and/or * carries an appropriate MXBean annotation). * @param clazz the class to check * @return whether there is an MXBean interface for the given class */
@Nullable public static Class<?> getMXBeanInterface(@Nullable Class<?> clazz) { if (clazz == null || clazz.getSuperclass() == null) { return null; } Class<?>[] implementedInterfaces = clazz.getInterfaces(); for (Class<?> iface : implementedInterfaces) { if (JMX.isMXBeanInterface(iface)) { return iface; } } return getMXBeanInterface(clazz.getSuperclass()); } }