org.gridkit.jvmtool.MBeanHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sjk-core Show documentation
Show all versions of sjk-core Show documentation
Core classes for Swiss Java Knife tool
/**
* 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;
}
}
}