package com.arondor.common.management.mbean; import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.ReflectionException; import org.apache.log4j.Logger; import com.arondor.common.reflection.api.parser.AccessibleClassParser; import com.arondor.common.reflection.model.java.AccessibleClass; import com.arondor.common.reflection.model.java.AccessibleField; import com.arondor.common.reflection.model.java.AccessibleMethod; import com.arondor.common.reflection.parser.java.JavaAccessibleClassParser; /** * Simple class helper for MBeanObject, containing utility methods for * MBeanObject manipulation * * @author Francois Barre * */ public class MBeanObjectHelper { /** * Logger stuff */ private static final Logger LOG = Logger.getLogger(MBeanObjectHelper.class); /** * Expensive log for this class */ private static final boolean verbose = LOG.isDebugEnabled(); private int maximumDuplicateObjects = 1024; private final AccessibleClassParser accessibleClassParser = new JavaAccessibleClassParser(); private final Map<String, MBeanInfo> mbeanInfoCache = new HashMap<String, MBeanInfo>(); private static final MBeanObjectHelper SINGLETON = new MBeanObjectHelper(); public static final MBeanObjectHelper getSingleton() { return SINGLETON; } /** * Generate MBean Information * * @param assignedObject * the exposed mbean object * @param description * the object description * @return MBeanInfo the MBean information describing this object */ public MBeanInfo getMBeanInfo(Object assignedObject, String description) { String className = assignedObject.getClass().getName(); if (mbeanInfoCache.containsKey(className)) { return mbeanInfoCache.get(className); } if (verbose) { LOG.debug("Building MBeanInfo : " + className); } AccessibleClass accessibleClass = accessibleClassParser.parseAccessibleClass(assignedObject.getClass()); MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[accessibleClass.getAccessibleFields().size()]; int idx = 0; for (AccessibleField fieldInfo : accessibleClass.getAccessibleFields().values()) { attributes[idx++] = toMBeanAttributeInfo(fieldInfo); } MBeanConstructorInfo[] constructors = null; MBeanOperationInfo[] operations = new MBeanOperationInfo[accessibleClass.getAccessibleMethods().size()]; idx = 0; for (AccessibleMethod accessibleMethod : accessibleClass.getAccessibleMethods()) { operations[idx++] = new MBeanOperationInfo(accessibleMethod.getName(), toMethod(assignedObject.getClass(), accessibleMethod)); } MBeanNotificationInfo[] notifications = null; MBeanInfo mbeanInfo = new MBeanInfo(className, description, attributes, constructors, operations, notifications); mbeanInfoCache.put(className, mbeanInfo); return mbeanInfo; } private Method toMethod(Class<?> clazz, AccessibleMethod accessibleMethod) { for (Method mth : clazz.getMethods()) { if (mth.getName().equals(accessibleMethod.getName())) { return mth; } } return null; } private MBeanAttributeInfo toMBeanAttributeInfo(AccessibleField accessibleField) { return new MBeanAttributeInfo(accessibleField.getName(), accessibleField.getClassName(), accessibleField.getDescription(), accessibleField.getReadable(), accessibleField.getWritable(), accessibleField.isIs()); } /** * Register an object * * @param clazz * the class to type object to * @param o * the name of this object * @param name * @return */ public synchronized ObjectName registerMBean(Class<?> clazz, Object o, String name) { name = name.replace(':', '_'); name = name.replace(' ', '_'); name = name.replace('=', '_'); try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); for (int iSuffix = 0; iSuffix < maximumDuplicateObjects; iSuffix++) { String suffix = (iSuffix == 0) ? "" : ("#" + iSuffix); ObjectName objectName = new ObjectName(clazz.getName() + ":type=" + name + suffix); if (mbs.isRegistered(objectName)) { LOG.debug("ObjectName '" + objectName + "' already registered."); continue; } try { mbs.registerMBean(o, objectName); } catch (javax.management.InstanceAlreadyExistsException iaee) { LOG.warn("Instance already exists '" + objectName + "', exception=" + iaee.getMessage()); continue; } LOG.info("Registered : '" + objectName + "'"); return objectName; } } catch (Exception e) { LOG.error( "Could not register MBean class='" + clazz.getName() + "', name='" + name + "' : " + e.getMessage(), e); } return null; } /** * Unregister MBean * * @param objectName * the MBean name to unregister */ public synchronized void unregisterMBean(ObjectName objectName) { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); mbs.unregisterMBean(objectName); } catch (Exception e) { LOG.error("Could not unregister MBean : " + objectName.toString() + " : " + e.getMessage(), e); } } /** * Get attribute * * @param assignedObject * @param arg0 * @return * @throws AttributeNotFoundException * @throws MBeanException * @throws ReflectionException */ public Object getAttribute(Object assignedObject, String arg0) throws AttributeNotFoundException, MBeanException, ReflectionException { String methodName = accessibleClassParser.attributeToGetter(arg0); try { Method getterMethod = assignedObject.getClass().getMethod(methodName, new Class<?>[0]); return getterMethod.invoke(assignedObject, new Object[0]); } catch (Exception e) { String isMethodName = accessibleClassParser.booleanAttributeToGetter(arg0); try { Method getterMethod = assignedObject.getClass().getMethod(isMethodName, new Class<?>[0]); return getterMethod.invoke(assignedObject, new Object[0]); } catch (Exception e2) { throw new ReflectionException(e2, "Getting attribute : '" + arg0 + "' (method name='" + methodName + "', '" + isMethodName + "')"); } } } public AttributeList getAttributes(Object assignedObject, String[] arg0) { AttributeList list = new AttributeList(); for (int i = 0; i < arg0.length; i++) { try { list.add(new Attribute(arg0[i], getAttribute(assignedObject, arg0[i]))); } catch (Exception e) { LOG.error("Could not add : " + arg0[i], e); } } return list; } /** * Invoke a method * * @param assignedObject * @param name * @param objects * @param signatureNames * @return * @throws MBeanException * @throws ReflectionException */ public Object invoke(Object assignedObject, String name, Object[] objects, String[] signatureNames) throws MBeanException, ReflectionException { LOG.debug("invoke(arg0=" + name + ")"); Class<?> signatures[] = new Class<?>[signatureNames.length]; for (int i = 0; i < signatureNames.length; i++) { try { signatures[i] = Class.forName(signatureNames[i]); } catch (ClassNotFoundException e) { throw new ReflectionException(e, "Could not get signature : " + signatureNames[i]); } } Method m; try { m = assignedObject.getClass().getMethod(name, signatures); } catch (SecurityException e) { throw new ReflectionException(new Exception("Could not get method '" + name + "'")); } catch (NoSuchMethodException e) { throw new ReflectionException(new Exception("Could not get method '" + name + "'")); } if (m == null) { throw new ReflectionException(new Exception("Could not get method '" + name + "'")); } try { return m.invoke(assignedObject, objects); } catch (Exception e) { LOG.error("Could not invoke method '" + name + "', " + e.getMessage(), e); throw new MBeanException(e, "Could not invoke method '" + name + "'"); } } protected Method getSetterMethodNoException(Object assignedObject, String methodName, Class<?> clazz) { Class<?> paramTypes[] = { clazz }; try { Method setterMethod = assignedObject.getClass().getMethod(methodName, paramTypes); return setterMethod; } catch (Throwable t) { return null; } } private Map<Class<?>, Class<?>> classMapAlias = new HashMap<Class<?>, Class<?>>(); private void putClassMapAlias(Class<?> c1, Class<?> c2) { classMapAlias.put(c1, c2); classMapAlias.put(c2, c1); } { putClassMapAlias(java.lang.Integer.class, int.class); putClassMapAlias(java.lang.Long.class, long.class); putClassMapAlias(java.lang.Boolean.class, boolean.class); putClassMapAlias(java.lang.Float.class, float.class); putClassMapAlias(java.lang.Double.class, double.class); } public String getPrimitiveClassMapAlias(String name) { if (name.equals("int")) return java.lang.Integer.class.getName(); if (name.equals("long")) return java.lang.Long.class.getName(); if (name.equals("boolean")) return java.lang.Boolean.class.getName(); if (name.equals("float")) return java.lang.Float.class.getName(); if (name.equals("double")) return java.lang.Double.class.getName(); return null; } public String getClassMapAlias(String name) { String primitive = getPrimitiveClassMapAlias(name); if (primitive != null) return primitive; try { Class<?> source = Class.forName(name); if (source == null) return null; } catch (ClassNotFoundException e) { return null; } Class<?> target = classMapAlias.get(name); if (target == null) return null; return target.getName(); } protected Method getSetterMethod(Object assignedObject, String methodName, Class<?> clazz) throws ReflectionException { for (Class<?> superclass = clazz; superclass != null; superclass = superclass.getSuperclass()) { Method setterMethod = getSetterMethodNoException(assignedObject, methodName, superclass); if (setterMethod != null) return setterMethod; Class<?> aliasClazz = classMapAlias.get(superclass); if (aliasClazz != null) { setterMethod = getSetterMethodNoException(assignedObject, methodName, aliasClazz); if (setterMethod != null) { if (verbose) { LOG.debug("methodName=" + methodName + ", using alias=" + aliasClazz.getName()); } return setterMethod; } } if (superclass.getInterfaces() != null) { for (Class<?> itfClazz : superclass.getInterfaces()) { setterMethod = getSetterMethodNoException(assignedObject, methodName, itfClazz); if (setterMethod != null) { if (verbose) { LOG.debug("methodName=" + methodName + ", using itf=" + itfClazz.getName()); } return setterMethod; } } } } throw new ReflectionException(new Exception("Could not find method '" + methodName + "(" + clazz.getName() + ")'"), "Reflection Exception on mbean=" + assignedObject); } /** * Set attribute * * @param assignedObject * @param arg0 * @throws AttributeNotFoundException * @throws InvalidAttributeValueException * @throws MBeanException * @throws ReflectionException */ public void setAttribute(Object assignedObject, Attribute arg0) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { String name = arg0.getName(); String methodName = accessibleClassParser.attributeToSetter(name); try { Method setterMethod = getSetterMethod(assignedObject, methodName, arg0.getValue().getClass()); if (verbose) { LOG.debug("mbean=" + assignedObject + ", name=" + name + ", clazz=" + arg0.getValue().getClass() + ", setterMethod=" + setterMethod); } Object params[] = { arg0.getValue() }; setterMethod.invoke(assignedObject, params); } catch (Exception e) { LOG.error("Exception while setting attribute : '" + arg0 + "'", e); throw new ReflectionException(e, "Setting attribute : '" + arg0 + "'"); } } /** * Set attribute list * * @param * @param arg0 * @return */ public AttributeList setAttributes(Object assignedObject, AttributeList arg0) { LOG.debug("Setting " + arg0.size() + " attributes."); AttributeList updatedList = new AttributeList(); for (Object attr : arg0) { try { setAttribute(assignedObject, (Attribute) attr); updatedList.add((Attribute) attr); } catch (Exception e) { LOG.error("Could not set : " + ((Attribute) attr).getName(), e); } } return updatedList; } }