package org.oddjob.jmx.general; import java.awt.Image; import java.io.IOException; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javax.management.Attribute; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.openmbean.CompositeData; import javax.swing.ImageIcon; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.DynaClass; import org.apache.commons.beanutils.DynaProperty; import org.apache.log4j.Logger; import org.oddjob.Describeable; import org.oddjob.Iconic; import org.oddjob.arooa.ClassResolver; import org.oddjob.arooa.convert.ArooaConversionException; import org.oddjob.images.IconEvent; import org.oddjob.images.IconListener; import org.oddjob.images.ImageIconStable; import org.oddjob.logging.LogEnabled; import org.oddjob.script.InvokerArguments; /** * A Simple implementation of an {@link MBeanNode}. * * @author rob * */ public class SimpleMBeanNode implements MBeanNode, Describeable, LogEnabled, Iconic { /** For logger names. */ private static final AtomicInteger instanceCount = new AtomicInteger(); /** The icon. */ private static final ImageIcon icon = new ImageIconStable(new ImageIcon( SimpleDomainNode.class.getResource("mbean.gif")).getImage( ).getScaledInstance(16, 16, Image.SCALE_SMOOTH), "MBean"); /** The logger for this instance. */ private final Logger logger = Logger.getLogger(getClass().getName() + "." + instanceCount.incrementAndGet()); /** Then name. */ private final ObjectName objectName; /** The server. */ private final MBeanServerConnection mBeanServer; /** For classes. */ private final ClassResolver classResolver; /** Info. */ private final MBeanInfo info; /** For DynaBean. */ private final ThisDynaClass dynaClass; /** * Constructor * * @param objectName * @param mBeanServer * @param classResolver * * @throws IntrospectionException * @throws InstanceNotFoundException * @throws ReflectionException * @throws IOException */ public SimpleMBeanNode(ObjectName objectName, MBeanServerConnection mBeanServer, ClassResolver classResolver) throws IntrospectionException, InstanceNotFoundException, ReflectionException, IOException { this.objectName = objectName; this.mBeanServer = mBeanServer; this.classResolver = classResolver; this.info = mBeanServer.getMBeanInfo(objectName); dynaClass = new ThisDynaClass(info.getAttributes()); } @Override public void initialise() { logger.info("MBean: " + objectName); logger.info("Attributes:"); MBeanAttributeInfo[] attributeInfo = info.getAttributes(); for (MBeanAttributeInfo attr : attributeInfo) { logger.info(" " + attr.getName() + ": " + attr.getType()); } logger.info("Operations:"); MBeanOperationInfo[] operationInfo = info.getOperations(); for (MBeanOperationInfo op : operationInfo) { StringBuilder params = new StringBuilder(); for (MBeanParameterInfo param : op.getSignature()) { if (params.length() > 0) { params.append(", "); } params.append(param.getType()); } logger.info(" " + op.getName() + "(" + params.toString() + "): " + op.getReturnType()); } } /* * (non-Javadoc) * @see org.oddjob.logging.LogEnabled#loggerName() */ @Override public String loggerName() { return logger.getName(); } /* * (non-Javadoc) * @see org.oddjob.script.Invoker#invoke(java.lang.String, org.oddjob.script.InvokerArguments) */ @Override public Object invoke(String name, InvokerArguments args) throws Exception { MBeanOperationInfo[] opInfos = info.getOperations(); MBeanOperationInfo match = null; for (MBeanOperationInfo info : opInfos) { if (name.equals(info.getName()) && args.size()== info.getSignature().length) { if (match != null) { throw new IllegalArgumentException( "Failed to find a unique method " + name + " with " + args.size() + " args. (We don't do overriding yet!)"); } match = info; } } if (match == null) { throw new IllegalArgumentException( "Failed to find a method " + name + " with " + args.size() + " args."); } MBeanParameterInfo[] paramInfo = match.getSignature(); String[] signature = new String[paramInfo.length]; Object[] converted = new Object[signature.length]; for (int i = 0; i < signature.length; ++i) { signature[i] = paramInfo[i].getType(); Class<?> type = classResolver.findClass(signature[i]); if (type == null) { throw new RuntimeException("Can not resolve class " + signature[i] + " for argument " + i); } try { converted[i] = args.getArgument(i, type); } catch (ArooaConversionException e) { throw new IllegalArgumentException( "Failed to convert argument" + i, e); } } try { logger.info("Invoking " + name + " with args " + Arrays.toString(converted)); Object result = mBeanServer.invoke( objectName, name, converted, signature); logger.info("Succesfully invoked " + name + ", result " + result); return result; } catch (Exception e) { logger.warn("Failed invoking" + name + "." + Arrays.toString(converted)); throw e; } } private static final String MBEAN_ICON = "mbean"; @Override public void addIconListener(IconListener listener) { listener.iconEvent(new IconEvent(this, MBEAN_ICON)); } @Override public ImageIcon iconForId(String id) { return icon; } @Override public void removeIconListener(IconListener listener) { } @Override public String toString() { return objectName.toString(); } @Override public boolean contains(String arg0, String arg1) { return false; } @Override public Object get(String name) { try { Object result = mBeanServer.getAttribute(objectName, name); if (result instanceof CompositeData) { return new CompositeDataDynaBean( (CompositeData) result); } else { return result; } } catch (Exception e) { throw new RuntimeException("Failed to get attribute " + name, e); } } @Override public Object get(String arg0, int arg1) { throw new RuntimeException("No indexed properties."); } @Override public Object get(String arg0, String arg1) { throw new RuntimeException("No mapped properties."); } @Override public DynaClass getDynaClass() { return dynaClass; } @Override public void remove(String arg0, String arg1) { throw new RuntimeException("No mapped properties."); } @Override public void set(String name, Object value) { try { logger.info("Setting " + name + " to " + value); mBeanServer.setAttribute(objectName, new Attribute(name, value)); } catch (Exception e) { throw new RuntimeException("Failed to get attribute " + name, e); } } @Override public void set(String arg0, int arg1, Object arg2) { throw new RuntimeException("No indexed properties."); } @Override public void set(String arg0, String arg1, Object arg2) { throw new RuntimeException("No mapped properties."); } @Override public Map<String, String> describe() { Map<String, String> description = new LinkedHashMap<String, String>(); DynaProperty[] props = dynaClass.getDynaProperties(); for (DynaProperty prop : props) { Object value = get(prop.getName()); description.put(prop.getName(), value == null ? null : value.toString()); } return description; } /* * (non-Javadoc) * @see org.oddjob.jmx.client.Destroyable#destroy() */ @Override public void destroy() { } /** * The {@link DynaClass} implementation. */ private class ThisDynaClass implements Serializable, DynaClass { private static final long serialVersionUID = 2012080200L; private final AttributeDynaProperty[] properties; private final Map<String, AttributeDynaProperty> map = new HashMap<String, AttributeDynaProperty>(); public ThisDynaClass(MBeanAttributeInfo[] attributes) { this.properties = new AttributeDynaProperty[attributes.length]; for (int i = 0; i < properties.length; ++i) { MBeanAttributeInfo info = attributes[i]; AttributeDynaProperty property = new AttributeDynaProperty(info); properties[i] = property; map.put(property.getName(), property); } } @Override public DynaProperty[] getDynaProperties() { return properties; } @Override public DynaProperty getDynaProperty(String name) { return map.get(name); } @Override public String getName() { return SimpleMBeanNode.this.toString() ; } @Override public DynaBean newInstance() throws IllegalAccessException, InstantiationException { throw new InstantiationException( "Can't create new " + getName()); } } /** * The {@link DynaProperty} implementation. * */ private class AttributeDynaProperty extends DynaProperty { private static final long serialVersionUID = 2012080200L; private final MBeanAttributeInfo info; public AttributeDynaProperty(MBeanAttributeInfo info) { super(info.getName()); this.info = info; } @SuppressWarnings("rawtypes") @Override public Class getType() { return classResolver.findClass(info.getType()); } } }