All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gridkit.jvmtool.MBeanHelper Maven / Gradle / Ivy

There is a newer version: 0.23
Show newest version
/**
 * Copyright 2014 Alexey Ragozin
 *
 * 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.gridkit.jvmtool;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.management.Attribute;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
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.management.openmbean.TabularData;

public class MBeanHelper {
	
    public static String FORMAT_TABLE_COLUMN_WIDTH_THRESHOLD = "table.column.maxWidth";
    public static String FORMAT_COMPOSITE_FIELD_WIDTH_THRESHODL = "composite.field.maxWidth";
    
	private MBeanServerConnection mserver;
	
	private int widthThresholdTable = 40;
	private int widthThresholdComposite = 1000;
	
	public MBeanHelper(MBeanServerConnection connection) {
		this.mserver = connection;
	}
	
	public void setFormatingOption(String name, Object value) {
	    if (FORMAT_TABLE_COLUMN_WIDTH_THRESHOLD.equals(name)) {
	        widthThresholdTable = (Integer) value;
	    }
	    else if (FORMAT_COMPOSITE_FIELD_WIDTH_THRESHODL.equals(name)) {
	        widthThresholdComposite = (Integer) value;
	    }
	}

	/**
	 * Get MBean attributes metadata
	 * @param bean MBean attributes
	 * @param attrs Required attributes
	 * @param read Attributes must be readable
	 * @param write Attributes must be writable
	 * @return Map attribute name - attribute info
	 */
	private Map getAttributeInfos(ObjectName bean, Collection attrs, boolean read, boolean write) throws IntrospectionException, ReflectionException, InstanceNotFoundException, IOException {
		MBeanInfo mbinfo = mserver.getMBeanInfo(bean);
		// Convert array to map
		Map attrInfos = new HashMap(attrs.size());
		for(MBeanAttributeInfo attrInfo: mbinfo.getAttributes()) {
			attrInfos.put(attrInfo.getName(), attrInfo);
		}
		// Check required attributes and their read/write flag
		for(String attr:attrs) {
			MBeanAttributeInfo ai = attrInfos.get(attr);
			if (ai == null) {
				throw new IllegalArgumentException("No such attribute '" + attr + "'");
			}
			if (read && !ai.isReadable()) {
				throw new IllegalArgumentException("Attribute '" + attr + "' is write-only");
			}
			if (write && !ai.isWritable()) {
				throw new IllegalArgumentException("Attribute '" + attr + "' is not writeable");
			}
		}

		return attrInfos;
	}

	/**
	 * Get MBean attributes metadata
	 * @param bean MBean attributes
	 * @param attrs Required attributes
	 * @return Map attribut name - attribute value
	 */
	private Map getAttributes(ObjectName bean, Collection attrs) throws InstanceNotFoundException, ReflectionException, IOException {
		Map attrValues = new HashMap(attrs.size());
		for(Attribute attr:mserver.getAttributes(bean, attrs.toArray(new String[0])).asList()) {
			attrValues.put(attr.getName(), attr.getValue());
		}
		return attrValues;
	}

	public Map get(ObjectName bean, Collection attrs) throws Exception {
		Map attrInfos = getAttributeInfos(bean, attrs, true, false);
		Map attrRawValues = getAttributes(bean, attrs);
		Map attrValues = new HashMap(attrs.size());
		for(String attr: attrs) {
			String attrValue = format(attrRawValues.get(attr), attrInfos.get(attr).getType());
			attrValues.put(attr, attrValue);
		}
		return attrValues;
	}

	public void getAsTable(ObjectName bean, Collection attrs, MTable table) throws Exception {
		Map attrInfos = getAttributeInfos(bean, attrs, true, false);
		Map attrRawValues = getAttributes(bean, attrs);
		for(String attr: attrs) {
			Object v = attrRawValues.get(attr);
			List tableHeader = null;
			List tableRows = new ArrayList();
			if (v instanceof CompositeData[]) {
				CompositeData[] td = (CompositeData[]) v;
				if (td.length == 0) {
					continue;
				}
				List header = new ArrayList();
				for (String f : td[0].getCompositeType().keySet()) {
					if (!header.contains(f)) {
						header.add(f);
					}
				}
				tableHeader = header;
				for (Object row : td) {
					tableRows.add(formatRow((CompositeData) row, header));
				}
			} else if (v instanceof CompositeData) {
				CompositeData cd = (CompositeData) v;
				List header = new ArrayList();
				for (String f : cd.getCompositeType().keySet()) {
					if (!header.contains(f)) {
						header.add(f);
					}
				}
				tableHeader = header;
				tableRows.add(formatRow(cd, header));
			} else if (v instanceof TabularData) {
				TabularData td = (TabularData) v;
				td.getTabularType().getIndexNames();
				List header = new ArrayList(td.getTabularType().getIndexNames());
				for (String f : td.getTabularType().getRowType().keySet()) {
					if (!header.contains(f)) {
						header.add(f);
					}
				}
				tableHeader = header;
				for (Object row : td.values()) {
					tableRows.add(formatRow((CompositeData) row, header));
				}
			} else {
				tableHeader = Collections.singletonList("Value");
				tableRows.add(new String[]{formatLine(v, attrInfos.get(attr).getType())});
			}
			List tableHeader2 = new ArrayList(tableHeader.size() + 2);
			tableHeader2.add(0, "MBean");
			tableHeader2.add(1, "Attribute");
			tableHeader2.addAll(tableHeader);
			String[] hdr = tableHeader2.toArray(new String[0]);
			for(String[] tableRow:tableRows) {
				List tableCells = new ArrayList(tableRow.length +2);
				tableCells.add(bean.getCanonicalName());
				tableCells.add(attr);
				tableCells.addAll(Arrays.asList(tableRow));
				table.append(hdr, tableCells.toArray(new String[0]));
			}
		}
	}

	public void set(ObjectName bean, String attr, String value) throws Exception {
		Map attrInfos = getAttributeInfos(bean, Collections.singletonList(attr), false, true);
		Object ov = convert(value, attrInfos.get(attr).getType());
		mserver.setAttribute(bean, new Attribute(attr, ov));
	}
	
	public String invoke(ObjectName bean, String operation, String... params) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException, MBeanException {
		MBeanInfo mbinfo = mserver.getMBeanInfo(bean);
		MBeanOperationInfo op = null;
		for(MBeanOperationInfo oi: mbinfo.getOperations()) {
			if (oi.getName().equalsIgnoreCase(operation) && oi.getSignature().length == params.length) {
				if (op != null) {
					throw new IllegalArgumentException("Ambiguous " + operation + "/" + params.length + " operatition signature for " + bean);
				}
				op = oi;
			}
		}
		if (op == null) {
			throw new IllegalArgumentException("Operation " + operation + "/" + params.length + " not found for " + bean);
		}
		Object[] args = new Object[params.length];
		String[] sig = new String[params.length];
		for(int i = 0; i != params.length; ++i) {
			args[i] = convert(params[i], op.getSignature()[i].getType());
			sig[i] = op.getSignature()[i].getType();
		}
		return format(mserver.invoke(bean, op.getName(), args, sig), op.getReturnType());
	}
	
	private String format(Object v, String type) {
		if (type.equals("void")) {
			return null;
		}
		else if (v instanceof CompositeData[]) {
			CompositeData[] td = (CompositeData[]) v;
			if (td.length == 0) {
				return "";
			}
			List header = new ArrayList();
			for(String f: td[0].getCompositeType().keySet()) {
				if (!header.contains(f)) {
					header.add(f);
				}
			}
			List content = new ArrayList();
			content.add(header.toArray(new String[0]));
			for(Object row: td) {
				content.add(formatRow((CompositeData)row, header));
			}
			return formatTable(content, widthThresholdTable, true);			
		}
		else if (v instanceof TabularData) {
			TabularData td = (TabularData) v;
			td.getTabularType().getIndexNames();
			List header = new ArrayList(td.getTabularType().getIndexNames());
			for(String f: td.getTabularType().getRowType().keySet()) {
				if (!header.contains(f)) {
					header.add(f);
				}
			}
			List content = new ArrayList();
			content.add(header.toArray(new String[0]));
			for(Object row: td.values()) {
				content.add(formatRow((CompositeData)row, header));
			}
			return formatTable(content, widthThresholdTable, true);
		}
		else if (v instanceof CompositeData) {
			CompositeData cd = (CompositeData)v;
			List content = new ArrayList();
			for(String field: cd.getCompositeType().keySet()) {
				String val = formatLine(cd.get(field), cd.getCompositeType().getType(field).getClassName());
				content.add(new String[]{field + ": ", val});
			}
			return formatTable(content, widthThresholdComposite, false);
		}
		else {
			return formatLine(v, type);
		}
	}

	private String formatTable(List content, int maxCell, boolean table) {
		int[] width = new int[content.get(0).length];
		for(String[] row: content) {
			for(int i = 0; i != row.length; ++i) {
				width[i] = Math.min(Math.max(width[i], row[i].length()), maxCell);
			}
		}

		StringBuilder sb = new StringBuilder();
		boolean header = table;
		for(String[] row: content) {
			for(int i = 0; i != width.length; ++i) {
				String cell = row[i];
				if (cell.length() > width[i]) {
					cell = cell.substring(0, width[i] - 3) + "...";
				}
				sb.append(cell);
				for(int s = 0; s != width[i] - cell.length(); ++s) {
					sb.append(' ');
				}
				if (table) {
					sb.append('|');
				}
			}
			if (table) {
				sb.setLength(sb.length() - 1);
			}
			sb.append('\n');
			if (header) {
				header = false;
				for(int n: width) {
					for(int i = 0; i != n; ++i) {
						sb.append('-');
					}
					sb.append('+');
				}
				sb.setLength(sb.length() - 1);
				sb.append('\n');
			}
		}
		
		return sb.toString();
	}
	
	private String formatLine(Object v, String type) {
		if (v instanceof TabularData) {
			TabularData td = (TabularData)v;
			StringBuilder sb = new StringBuilder();
			for(Object c: td.values()) {
				sb.append(formatLine(c, td.getTabularType().getRowType().getClassName()));
				sb.append(",");
			}
			if (sb.length() > 0) {
				sb.setLength(sb.length() - 1);
			}
			return sb.toString();
		}
		if (v instanceof CompositeData[]) {
			CompositeData[] td = (CompositeData[])v;
			StringBuilder sb = new StringBuilder();
			for(Object c: td) {
				sb.append(formatLine(c, ((CompositeData)c).getCompositeType().getClassName()));
				sb.append(",");
			}
			if (sb.length() > 0) {
				sb.setLength(sb.length() - 1);
			}
			return sb.toString();
		}
		else if (v instanceof CompositeData) {
			CompositeData cdata = (CompositeData) v;
			StringBuilder sb = new StringBuilder();
			sb.append("{");
			for(String attr: cdata.getCompositeType().keySet()) {
				sb.append(attr).append("=");
				sb.append(formatLine(cdata.get(attr), cdata.getCompositeType().getType(attr).getClassName()));
				sb.append(',');
			}
			if (sb.length() > 1) {
				sb.setLength(sb.length() - 1);
			}
			sb.append("}");
			return sb.toString();
		}
		else if (v instanceof Object[]) {
			return Arrays.toString((Object[])v);
		}
		else if (v instanceof boolean[]) {
			return Arrays.toString((boolean[])v);
		}
		else if (v instanceof byte[]) {
			return Arrays.toString((byte[])v);
		}
		else if (v instanceof char[]) {
			return Arrays.toString((char[])v);
		}
		else if (v instanceof short[]) {
			return Arrays.toString((short[])v);
		}
		else if (v instanceof int[]) {
			return Arrays.toString((int[])v);
		}
		else if (v instanceof long[]) {
			return Arrays.toString((long[])v);
		}
		else if (v instanceof float[]) {
			return Arrays.toString((float[])v);
		}
		else if (v instanceof double[]) {
			return Arrays.toString((double[])v);
		}
		else {
			return String.valueOf(v);
		}
	}

	private String[] formatRow(CompositeData row, List header) {
		String[] text = new String[header.size()];
		for(int i = 0; i != text.length; ++i) {
			String attr = header.get(i);
			text[i] = formatLine(row.get(attr), row.getCompositeType().getType(attr).getClassName());
		}
		return text;
	}

	private Object convert(String value, String type) {
		if (type.equals("java.lang.String")) {
			if ("".equals(value.trim()))
				return null;
			return value;
		}
		if (type.equals("boolean") || type.equals("java.lang.Boolean")) {
			if (type.equals("java.lang.Boolean") && value.trim().isEmpty())
				return null;
			return Boolean.valueOf(value);
		}
		else if (type.equals("byte") || type.equals("java.lang.Byte")) {
			if (type.equals("java.lang.Byte") && value.trim().isEmpty())
				return null;
			return Byte.valueOf(value);
		}
		else if (type.equals("short") || type.equals("java.lang.Short")) {
			if (type.equals("java.lang.Short") && value.trim().isEmpty())
				return null;
			return Short.valueOf(value);
		}
		else if (type.equals("char") || type.equals("java.lang.Character")) {
			if (type.equals("java.lang.Character") && value.trim().isEmpty())
				return null;
			if (value.length() == 1) {
				return value.charAt(0);
			}
			else {
				throw new IllegalArgumentException("Cannot convert '" + value + "' to " + type);
			}
		}
		else if (type.equals("int") || type.equals("java.lang.Integer")) {
			if (type.equals("java.lang.Integer") && value.trim().isEmpty())
				return null;
			return Integer.valueOf(value);
		}
		else if (type.equals("long") || type.equals("java.lang.Long")) {
			if (type.equals("java.lang.Long") && value.trim().isEmpty())
				return null;
			return Long.valueOf(value);
		}
		else if (type.equals("float") || type.equals("java.lang.Float")) {
			if (type.equals("java.lang.Float") && value.trim().isEmpty())
				return null;
			return Float.valueOf(value);
		}
		else if (type.equals("double") || type.equals("java.lang.Double")) {
			if (type.equals("java.lang.Double") && value.trim().isEmpty())
				return null;
			return Double.valueOf(value);
		}
		else if (type.startsWith("[")) {
			if (value.trim().isEmpty())
				return null;
			String[] elements = value.split("[,]");
			Object array = ARRAY_MAP.get(type);
			if (array == null) {
				throw new IllegalArgumentException("Cannot convert '" + value + "' to " + type);
			}
			array = Array.newInstance(array.getClass().getComponentType(), elements.length);
			String etype = array.getClass().getComponentType().getName();
			for(int i = 0; i != elements.length; ++i) {
				Array.set(array, i, convert(elements[i], etype));
			}
			return array;			
		}
		throw new IllegalArgumentException("Cannot convert '" + value + "' to " + type);
	}

	public String describe(ObjectName bean) throws Exception {
		MBeanInfo mbinfo = mserver.getMBeanInfo(bean);
		StringBuilder sb = new StringBuilder();
		sb.append(bean);
		sb.append('\n');
		sb.append(mbinfo.getClassName());
		sb.append('\n');
		sb.append(" - " + mbinfo.getDescription());
		sb.append('\n');
		for(MBeanAttributeInfo ai: mbinfo.getAttributes()) {
			sb.append(" (A) ");
			sb.append(ai.getName()).append(" : ").append(toPrintableType(ai.getType())).append("");
			if (!ai.isReadable()) {
				sb.append(" - WRITEONLY");
			}
			else if (ai.isWritable()) {
				sb.append(" - WRITEABLE");
			}
			sb.append('\n');
			if (!ai.getName().equals(ai.getDescription())) {
				sb.append("  - " + ai.getDescription());
				sb.append('\n');
			}
		}
		for (MBeanOperationInfo oi: mbinfo.getOperations()) {
			sb.append(" (O) ");
			sb.append(oi.getName()).append("(");
			for(MBeanParameterInfo pi: oi.getSignature()) {
				String name = pi.getName();
				String type = toPrintableType(pi.getType());
				sb.append(type).append(' ').append(name).append(", ");
			}
			if (oi.getSignature().length > 0) {
				sb.setLength(sb.length() - 2);
			}
			sb.append(") : ").append(toPrintableType(oi.getReturnType()));
			sb.append('\n');
			if (!oi.getName().equals(oi.getDescription())) {
				sb.append("  - " + oi.getDescription());
				sb.append('\n');
			}
		}
		return sb.toString();
	}

	static Map ARRAY_MAP = new HashMap();
	static {
		ARRAY_MAP.put("[Z", new boolean[0]);
		ARRAY_MAP.put("[B", new byte[0]);
		ARRAY_MAP.put("[S", new short[0]);
		ARRAY_MAP.put("[C", new char[0]);
		ARRAY_MAP.put("[I", new int[0]);
		ARRAY_MAP.put("[J", new long[0]);
		ARRAY_MAP.put("[F", new float[0]);
		ARRAY_MAP.put("[D", new double[0]);
		ARRAY_MAP.put("[Ljava.lang.String;", new String[0]);
	}

	static Map TYPE_MAP = new HashMap();
	static {
		TYPE_MAP.put("java.lang.String", "String");
		TYPE_MAP.put("javax.management.openmbean.CompositeData", "CompositeData");
		TYPE_MAP.put("javax.management.openmbean.TabularData", "TabularData");
		TYPE_MAP.put("[Z", "boolean[]");
		TYPE_MAP.put("[B", "byte[]");
		TYPE_MAP.put("[S", "short[]");
		TYPE_MAP.put("[C", "char[]");
		TYPE_MAP.put("[I", "int[]");
		TYPE_MAP.put("[J", "long[]");
		TYPE_MAP.put("[F", "float[]");
		TYPE_MAP.put("[D", "double[]");
	}
	
	static String toPrintableType(String type) {
		if (TYPE_MAP.containsKey(type)) {
			return TYPE_MAP.get(type);
		}
		else if (type.startsWith("[L")) {
			return toPrintableType(type.substring(2, type.length() -1)) + "[]";
		}
		else {
			return type;
		}
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy