001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jmx.support;
018:
019: import java.beans.PropertyDescriptor;
020: import java.lang.management.ManagementFactory;
021: import java.lang.reflect.Method;
022: import java.util.Hashtable;
023: import java.util.List;
024:
025: import javax.management.DynamicMBean;
026: import javax.management.MBeanParameterInfo;
027: import javax.management.MBeanServer;
028: import javax.management.MBeanServerFactory;
029: import javax.management.MalformedObjectNameException;
030: import javax.management.ObjectName;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: import org.springframework.core.JdkVersion;
036: import org.springframework.jmx.MBeanServerNotFoundException;
037: import org.springframework.util.ClassUtils;
038: import org.springframework.util.ObjectUtils;
039: import org.springframework.util.StringUtils;
040:
041: /**
042: * Collection of generic utility methods to support Spring JMX.
043: * Includes a convenient method to locate an MBeanServer.
044: *
045: * @author Rob Harrop
046: * @author Juergen Hoeller
047: * @since 1.2
048: * @see #locateMBeanServer
049: */
050: public abstract class JmxUtils {
051:
052: /**
053: * The key used when extending an existing {@link ObjectName} with the
054: * identity hash code of its corresponding managed resource.
055: */
056: public static final String IDENTITY_OBJECT_NAME_KEY = "identity";
057:
058: /**
059: * Suffix used to identify an MBean interface
060: */
061: private static final String MBEAN_SUFFIX = "MBean";
062:
063: private static final Log logger = LogFactory.getLog(JmxUtils.class);
064:
065: /**
066: * Attempt to find a locally running <code>MBeanServer</code>. Fails if no
067: * <code>MBeanServer</code> can be found. Logs a warning if more than one
068: * <code>MBeanServer</code> found, returning the first one from the list.
069: * @return the <code>MBeanServer</code> if found
070: * @throws org.springframework.jmx.MBeanServerNotFoundException
071: * if no <code>MBeanServer</code> could be found
072: * @see javax.management.MBeanServerFactory#findMBeanServer
073: */
074: public static MBeanServer locateMBeanServer()
075: throws MBeanServerNotFoundException {
076: return locateMBeanServer(null);
077: }
078:
079: /**
080: * Attempt to find a locally running <code>MBeanServer</code>. Fails if no
081: * <code>MBeanServer</code> can be found. Logs a warning if more than one
082: * <code>MBeanServer</code> found, returning the first one from the list.
083: * @param agentId the agent identifier of the MBeanServer to retrieve.
084: * If this parameter is <code>null</code>, all registered MBeanServers are
085: * considered.
086: * @return the <code>MBeanServer</code> if found
087: * @throws org.springframework.jmx.MBeanServerNotFoundException
088: * if no <code>MBeanServer</code> could be found
089: * @see javax.management.MBeanServerFactory#findMBeanServer(String)
090: */
091: public static MBeanServer locateMBeanServer(String agentId)
092: throws MBeanServerNotFoundException {
093: List servers = MBeanServerFactory.findMBeanServer(agentId);
094:
095: MBeanServer server = null;
096: if (servers != null && servers.size() > 0) {
097: // Check to see if an MBeanServer is registered.
098: if (servers.size() > 1 && logger.isWarnEnabled()) {
099: logger.warn("Found more than one MBeanServer instance"
100: + (agentId != null ? " with agent id ["
101: + agentId + "]" : "")
102: + ". Returning first from list.");
103: }
104: server = (MBeanServer) servers.get(0);
105: }
106:
107: if (server == null && agentId == null
108: && JdkVersion.isAtLeastJava15()) {
109: // Attempt to load the PlatformMBeanServer.
110: try {
111: server = ManagementFactory.getPlatformMBeanServer();
112: } catch (SecurityException ex) {
113: throw new MBeanServerNotFoundException(
114: "No specific MBeanServer found, "
115: + "and not allowed to obtain the Java platform MBeanServer",
116: ex);
117: }
118: }
119:
120: if (server == null) {
121: throw new MBeanServerNotFoundException(
122: "Unable to locate an MBeanServer instance"
123: + (agentId != null ? " with agent id ["
124: + agentId + "]" : ""));
125: }
126:
127: if (logger.isDebugEnabled()) {
128: logger.debug("Found MBeanServer: " + server);
129: }
130: return server;
131: }
132:
133: /**
134: * Convert an array of <code>MBeanParameterInfo</code> into an array of
135: * <code>Class</code> instances corresponding to the parameters.
136: * @param paramInfo the JMX parameter info
137: * @return the parameter types as classes
138: * @throws ClassNotFoundException if a parameter type could not be resolved
139: */
140: public static Class[] parameterInfoToTypes(
141: MBeanParameterInfo[] paramInfo)
142: throws ClassNotFoundException {
143: Class[] types = null;
144: if (paramInfo != null && paramInfo.length > 0) {
145: types = new Class[paramInfo.length];
146: for (int x = 0; x < paramInfo.length; x++) {
147: types[x] = ClassUtils.forName(paramInfo[x].getType());
148: }
149: }
150: return types;
151: }
152:
153: /**
154: * Create a <code>String[]</code> representing the argument signature of a
155: * method. Each element in the array is the fully qualified class name
156: * of the corresponding argument in the methods signature.
157: * @param method the method to build an argument signature for
158: * @return the signature as array of argument types
159: */
160: public static String[] getMethodSignature(Method method) {
161: Class[] types = method.getParameterTypes();
162: String[] signature = new String[types.length];
163: for (int x = 0; x < types.length; x++) {
164: signature[x] = types[x].getName();
165: }
166: return signature;
167: }
168:
169: /**
170: * Return the JMX attribute name to use for the given JavaBeans property.
171: * <p>When using strict casing, a JavaBean property with a getter method
172: * such as <code>getFoo()</code> translates to an attribute called
173: * <code>Foo</code>. With strict casing disabled, <code>getFoo()</code>
174: * would translate to just <code>foo</code>.
175: * @param property the JavaBeans property descriptor
176: * @param useStrictCasing whether to use strict casing
177: * @return the JMX attribute name to use
178: */
179: public static String getAttributeName(PropertyDescriptor property,
180: boolean useStrictCasing) {
181: if (useStrictCasing) {
182: return StringUtils.capitalize(property.getName());
183: } else {
184: return property.getName();
185: }
186: }
187:
188: /**
189: * Determine whether the given bean class qualifies as an MBean as-is.
190: * <p>This implementation checks for {@link javax.management.DynamicMBean}
191: * classes as well as classes with corresponding "*MBean" interface
192: * (Standard MBeans).
193: * @param beanClass the bean class to analyze
194: * @return whether the class qualifies as an MBean
195: * @see org.springframework.jmx.export.MBeanExporter#isMBean(Class)
196: */
197: public static boolean isMBean(Class beanClass) {
198: if (beanClass == null) {
199: return false;
200: }
201: if (DynamicMBean.class.isAssignableFrom(beanClass)) {
202: return true;
203: }
204: Class cls = beanClass;
205: while (cls != null && cls != Object.class) {
206: if (hasMBeanInterface(cls)) {
207: return true;
208: }
209: cls = cls.getSuperclass();
210: }
211: return false;
212: }
213:
214: /**
215: * Return the class or interface to expose for the given bean.
216: * This is the class that will be searched for attributes and operations
217: * (for example, checked for annotations).
218: * <p>This implementation returns the superclass for a CGLIB proxy and
219: * the class of the given bean else (for a JDK proxy or a plain bean class).
220: * @param managedBean the bean instance (might be an AOP proxy)
221: * @return the bean class to expose
222: * @see org.springframework.util.ClassUtils#getUserClass(Object)
223: */
224: public static Class getClassToExpose(Object managedBean) {
225: return ClassUtils.getUserClass(managedBean);
226: }
227:
228: /**
229: * Return the class or interface to expose for the given bean class.
230: * This is the class that will be searched for attributes and operations
231: * (for example, checked for annotations).
232: * <p>This implementation returns the superclass for a CGLIB proxy and
233: * the class of the given bean else (for a JDK proxy or a plain bean class).
234: * @param beanClass the bean class (might be an AOP proxy class)
235: * @return the bean class to expose
236: * @see org.springframework.util.ClassUtils#getUserClass(Class)
237: */
238: public static Class getClassToExpose(Class beanClass) {
239: return ClassUtils.getUserClass(beanClass);
240: }
241:
242: /**
243: * Return whether a Standard MBean interface exists for the given class
244: * (that is, an interface whose name matches the class name of the
245: * given class but with suffix "MBean).
246: * @param clazz the class to check
247: * @return whether there is a Standard MBean interface for the given class
248: */
249: private static boolean hasMBeanInterface(Class clazz) {
250: Class[] implementedInterfaces = clazz.getInterfaces();
251: String mbeanInterfaceName = clazz.getName() + MBEAN_SUFFIX;
252: for (int x = 0; x < implementedInterfaces.length; x++) {
253: Class iface = implementedInterfaces[x];
254: if (iface.getName().equals(mbeanInterfaceName)) {
255: return true;
256: }
257: }
258: return false;
259: }
260:
261: /**
262: * Append an additional key/value pair to an existing {@link ObjectName} with the key being
263: * the static value <code>identity</code> and the value being the identity hash code of the
264: * managed resource being exposed on the supplied {@link ObjectName}. This can be used to
265: * provide a unique {@link ObjectName} for each distinct instance of a particular bean or
266: * class. Useful when generating {@link ObjectName ObjectNames} at runtime for a set of
267: * managed resources based on the template value supplied by a
268: * {@link org.springframework.jmx.export.naming.ObjectNamingStrategy}.
269: * @param objectName the original JMX ObjectName
270: * @param managedResource the MBean instance
271: * @return an ObjectName with the MBean identity added
272: * @throws MalformedObjectNameException in case of an invalid object name specification
273: * @see org.springframework.util.ObjectUtils#getIdentityHexString(Object)
274: */
275: public static ObjectName appendIdentityToObjectName(
276: ObjectName objectName, Object managedResource)
277: throws MalformedObjectNameException {
278:
279: Hashtable keyProperties = objectName.getKeyPropertyList();
280: keyProperties.put(IDENTITY_OBJECT_NAME_KEY, ObjectUtils
281: .getIdentityHexString(managedResource));
282: return ObjectNameManager.getInstance(objectName.getDomain(),
283: keyProperties);
284: }
285:
286: }
|