/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2007], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.product.jmx; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.hyperic.hq.product.PluginException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class MBeanUtil { private static Log log = LogFactory.getLog(MBeanUtil.class); public static final String DYNAMIC_SERVICE_DOMAIN = "spring.application"; private static Map converters = new HashMap(); static { initConverters(); } //convert a String to common types public interface Converter { public Object convert(String param); } public interface ListConverter { public Object convert(String[] params); } public static void addConverter(Class type, Converter converter) { converters.put(type.getName(), converter); } public static void addConverter(Class type, ListConverter converter) { converters.put(type.getName(), converter); } private static void addConverter(Class addType, Class fromType) { converters.put(addType.getName(), converters.get(fromType.getName())); } private static IllegalArgumentException invalid(String param, Exception e) { return new IllegalArgumentException("'" + param + "': " + e.getMessage()); } private static void initConverters() { addConverter(Object.class, new Converter() { public Object convert(String param) { return param; } }); addConverter(Short.class, new Converter() { public Object convert(String param) { return Short.valueOf(param); } }); addConverter(Integer.class, new Converter() { public Object convert(String param) { return Integer.valueOf(param); } }); addConverter(Long.class, new Converter() { public Object convert(String param) { return Long.valueOf(param); } }); addConverter(Double.class, new Converter() { public Object convert(String param) { return Double.valueOf(param); } }); addConverter(Boolean.class, new Converter() { public Object convert(String param) { return Boolean.valueOf(param); } }); addConverter(File.class, new Converter() { public Object convert(String param) { return new File(param); } }); addConverter(URL.class, new Converter() { public Object convert(String param) { try { return new URL(param); } catch (MalformedURLException e) { throw invalid(param, e); } } }); addConverter(ObjectName.class, new Converter() { public Object convert(String param) { try { return new ObjectName(param); } catch (MalformedObjectNameException e) { throw invalid(param, e); } } }); addConverter(List.class, new ListConverter() { public Object convert(String[] params) { return Arrays.asList(params); } }); addConverter(String[].class, new ListConverter() { public Object convert(String[] params) { return params; } }); addConverter(Long[].class, new ListConverter() { public Object convert(String[] params) { Long[] args = new Long[params.length]; for (int i=0; i<params.length; i++) { args[i] = Long.valueOf(params[i]); } return args; } }); addConverter(Integer[].class, new ListConverter() { public Object convert(String[] params) { Integer[] args = new Integer[params.length]; for (int i=0; i<params.length; i++) { args[i] = Integer.valueOf(params[i]); } return args; } }); addConverter(Double[].class, new ListConverter() { public Object convert(String[] params) { Double[] args = new Double[params.length]; for (int i=0; i<params.length; i++) { args[i] = Double.valueOf(params[i]); } return args; } }); addConverter(Short[].class, new ListConverter() { public Object convert(String[] params) { Short[] args = new Short[params.length]; for (int i=0; i<params.length; i++) { args[i] = Short.valueOf(params[i]); } return args; } }); addConverter(Boolean[].class, new ListConverter() { public Object convert(String[] params) { Boolean[] args = new Boolean[params.length]; for (int i=0; i<params.length; i++) { args[i] = Boolean.valueOf(params[i]); } return args; } }); addConverter(long[].class, new ListConverter() { public Object convert(String[] params) { long[] args = new long[params.length]; for (int i=0; i<params.length; i++) { args[i] = Long.parseLong(params[i]); } return args; } }); addConverter(int[].class, new ListConverter() { public Object convert(String[] params) { int[] args = new int[params.length]; for (int i=0; i<params.length; i++) { args[i] = Integer.parseInt(params[i]); } return args; } }); addConverter(double[].class, new ListConverter() { public Object convert(String[] params) { double[] args = new double[params.length]; for (int i=0; i<params.length; i++) { args[i] = Double.parseDouble(params[i]); } return args; } }); addConverter(short[].class, new ListConverter() { public Object convert(String[] params) { short[] args = new short[params.length]; for (int i=0; i<params.length; i++) { args[i] = Short.parseShort(params[i]); } return args; } }); addConverter(boolean[].class, new ListConverter() { public Object convert(String[] params) { boolean[] args = new boolean[params.length]; for (int i=0; i<params.length; i++) { args[i] = params[i].equals("true") ? true : false; } return args; } }); Class[][] aliases = { { String.class, Object.class }, { Short.TYPE, Short.class }, { Integer.TYPE, Integer.class }, { Long.TYPE, Long.class }, { Double.TYPE, Double.class }, { Boolean.TYPE, Boolean.class }, }; for (int i=0; i<aliases.length; i++) { addConverter(aliases[i][0], aliases[i][1]); } } private static Object getConverter(String type) { Object converter = converters.get(type); if (converter == null) { converter = converters.get(Object.class.getName()); } return (Object)converter; } private static boolean hasConverter(String type) { return converters.get(type) != null; } private static Object convert(String type, String param) { return ((Converter)getConverter(type)).convert(param); } private static Object convert(String type, String[] params) { return ((ListConverter)getConverter(type)).convert(params); } private static boolean isListType(String type) { return getConverter(type) instanceof ListConverter; } public static class OperationParams { public Object[] arguments; public String[] signature; public boolean isAttribute = false; } private static PluginException invalidParams(String method, HashMap sigs, Object[]args) { StringBuffer num = new StringBuffer(); StringBuffer sig = new StringBuffer(); for (Iterator it=sigs.keySet().iterator(); it.hasNext();) { Object o = it.next(); StringBuffer sb; if (o instanceof Integer) { sb = num; } else { sb = sig; } if (sb.length() != 0) { sb.append(", "); } sb.append(o); } String msg = "operation '" + method + "' takes (" + num + ")" + " arguments, " + args.length + " given. Signature=[" + sig + "]"; return new PluginException(msg); } public static OperationParams getAttributeParams(MBeanInfo info, String method, Object args[]) throws PluginException { if (method.startsWith("set") || method.startsWith("get")) { method = method.substring(3); } MBeanAttributeInfo[] attrs = info.getAttributes(); for (int i=0; i<attrs.length; i++) { MBeanAttributeInfo attr = attrs[i]; if (!attr.getName().equals(method)) { continue; } String sig = attr.getType(); if (!hasConverter(sig)) { String msg = "Cannot convert String argument to " + sig; throw new PluginException(msg); } OperationParams params = new OperationParams(); Object value = null; try { if (args.length > 0) { value = convert(sig, (String)args[0]); } } catch (Exception e) { String msg = "Exception converting param '" + args[0] + "' to type '" + sig + "'"; throw new PluginException(msg + ": " + e); } if (value != null) { params.arguments = new Object[] { value }; } params.isAttribute = true; return params; } return null; } private static String toString(MBeanParameterInfo[] pinfo) { StringBuffer sb = new StringBuffer(); for (int i=0; i<pinfo.length; i++) { if (sb.length() != 0) { sb.append(", "); } sb.append(pinfo[i].getType()); } sb.insert(0, '('); sb.append(')'); return sb.toString(); } private static Object[] asObjectArray(long[] values) { Long[] list = new Long[values.length]; for (int i=0; i<values.length; i++) { list[i] = new Long(values[i]); } return list; } private static Object[] asObjectArray(int[] values) { Integer[] list = new Integer[values.length]; for (int i=0; i<values.length; i++) { list[i] = new Integer(values[i]); } return list; } private static Object[] asObjectArray(Object values) { if (values instanceof long[]) { return asObjectArray((long[])values); } else if (values instanceof int[]) { return asObjectArray((int[])values); } else if (values instanceof Object[]) { return (Object[])values; } else { return null; } } public static OperationParams getOperationParams(MBeanInfo info, String method, Object args[]) throws PluginException { boolean isDebug = log.isDebugEnabled(); MBeanOperationInfo[] ops = info.getOperations(); MBeanParameterInfo[] pinfo = null; HashMap sigs = new HashMap(); String methodSignature = null; if (args.length != 0) { String arg = (String)args[0]; if (arg.startsWith("@(") && arg.endsWith(")")) { methodSignature = arg.substring(1); String[] dst = new String[args.length-1]; System.arraycopy(args, 1, dst, 0, dst.length); args = dst; } } if (isDebug) { String msg = "Converting params for: " + method + Arrays.asList(args); if (methodSignature != null) { msg += ", using provided signature: " + methodSignature; } log.debug(msg); } for (int i=0; i<ops.length; i++) { if (ops[i].getName().equals(method)) { pinfo = ops[i].getSignature(); StringBuffer sig = new StringBuffer(); sig.append("("); for (int j=0; j<pinfo.length; j++) { sig.append(pinfo[j].getType()); if (j+1 != pinfo.length){ sig.append(';'); } } sig.append(')'); log.debug("Found operation: " + method + sig); sigs.put(sig.toString(), pinfo); sigs.put(new Integer(pinfo.length), pinfo); //XXX might have more than 1 method w/ same //number of args but different signature } } if (sigs.size() == 0) { OperationParams op = getAttributeParams(info, method, args); if (op != null) { return op; } String msg = "No MBean Operation or Attribute Info found for: " + method; throw new PluginException(msg); } else if (sigs.size() > 1) { if (methodSignature == null) { //try exact match, else last one wins. Object o = sigs.get(new Integer(args.length)); if (o != null) { pinfo = (MBeanParameterInfo[])o; if (log.isDebugEnabled()) { log.debug("Using default sig: " + toString(pinfo)); } } } else { pinfo = (MBeanParameterInfo[])sigs.get(methodSignature); if (pinfo == null) { String msg = "No matching Operation signature found for: " + method + methodSignature; throw new PluginException(msg); } else if (log.isDebugEnabled()) { log.debug("Using matched sig: " + toString(pinfo)); } } } int len = pinfo.length; int nargs = args.length; int consumed; String[] signature = new String[len]; List arguments = new ArrayList(); for (int i=0,j=0; i<len; i++, j+=consumed) { consumed = 1; String sig = pinfo[i].getType(); signature[i] = sig; if (!hasConverter(sig)) { String msg = "Cannot convert String argument to " + sig; throw new PluginException(msg); } if (j >= args.length) { throw invalidParams(method, sigs, args); } if (isListType(sig)) { String[] listArgs; if (len == 1) { listArgs = (String[])args; } else { int remain = (len-1) - j; consumed = args.length - j - remain; listArgs = new String[consumed]; System.arraycopy(args, j, listArgs, 0, consumed); } nargs -= listArgs.length; try { arguments.add(convert(sig, listArgs)); } catch (Exception e) { String msg = "Exception converting " + Arrays.asList(listArgs) + "' to type '" + sig + "'"; throw new PluginException(msg + ": " + e); } } else { nargs--; try { arguments.add(convert(sig, (String)args[j])); } catch (Exception e) { String msg = "Exception converting param[" + j + "] '" + args[j] + "' to type '" + sig + "'"; throw new PluginException(msg + ": " + e); } } if (isDebug) { Object arg = arguments.get(i); if (arg.getClass().isArray()) { if ((arg = asObjectArray(arg)) == null) { arg = arguments.get(i).toString(); } else { arg = Arrays.asList((Object[])arg).toString(); } } log.debug(method + "() arg " + i + "=" + arg + ", type=" + sig); } } if (nargs != 0) { throw invalidParams(method, sigs, args); } OperationParams params = new OperationParams(); params.signature = signature; params.arguments = arguments.toArray(); return params; } //where is commons-anything-tostring.jar? public static String anyToString(Object obj) { if (obj.getClass().isArray()) { if (Object[].class.isAssignableFrom(obj.getClass())) { return Arrays.asList((Object[])obj).toString(); } else { List values = new ArrayList(); if (obj.getClass() == long[].class) { long[] xv = (long[])obj; for (int i=0; i<xv.length; i++) { values.add(String.valueOf(xv[i])); } } else if (obj.getClass() == int[].class) { int[] xv = (int[])obj; for (int i=0; i<xv.length; i++) { values.add(String.valueOf(xv[i])); } } else if (obj.getClass() == short[].class) { short[] xv = (short[])obj; for (int i=0; i<xv.length; i++) { values.add(String.valueOf(xv[i])); } } else if (obj.getClass() == double[].class) { double[] xv = (double[])obj; for (int i=0; i<xv.length; i++) { values.add(String.valueOf(xv[i])); } } else if (obj.getClass() == boolean[].class) { boolean[] xv = (boolean[])obj; for (int i=0; i<xv.length; i++) { values.add(String.valueOf(xv[i])); } } else /*if (obj.getClass() == caveman[].class)*/ { return obj.toString(); } return values.toString(); } } else { return obj.toString(); } } }