package org.jboss.mx.capability;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.*;
import org.jboss.mx.metadata.AttributeOperationResolver;
import org.jboss.mx.server.ServerConstants;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ReflectionException;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class OptimizedMBeanDispatcher implements ServerConstants
{
final static Class SUPER_CLASS = ReflectedMBeanDispatcher.class;
public static ReflectedMBeanDispatcher create(MBeanInfo info, Object resource)
{
try
{
String className = resource.getClass().getName().replace('.', '_') + "_Dispatcher";
String superClass = SUPER_CLASS.getName();
String fileName = className + ".class";
int modifiers = Constants.ACC_PUBLIC;
String[] interfaces = new String[0];
ClassGen clazz = new ClassGen(className, superClass, fileName, modifiers, interfaces);
ConstantPoolGen cp = clazz.getConstantPool();
clazz.addMethod(createConstructor(cp, className).getMethod());
clazz.addMethod(createInvoke(cp, info, className, resource.getClass().getName()).getMethod());
clazz.update();
JavaClass c = clazz.getJavaClass();
ByteArrayOutputStream baos = new ByteArrayOutputStream(2000);
BufferedOutputStream bos = new BufferedOutputStream(baos);
c.dump(bos);
ClassLoader ocl = new DispatchClassLoader(resource.getClass().getClassLoader(), className, baos.toByteArray());
Class dispatcherClass = ocl.loadClass(className);
Constructor constr = dispatcherClass.getConstructor(
new Class[] { MBeanInfo.class, AttributeOperationResolver.class, Object.class }
);
Object o = constr.newInstance(new Object[] { info, new AttributeOperationResolver(info), resource });
return (ReflectedMBeanDispatcher)o;
}
catch (Exception e)
{
e.printStackTrace();
throw new Error();
}
}
public static String getMethodDescriptor(MBeanParameterInfo[] signature, String returnType)
{
StringBuffer sign = new StringBuffer(256);
sign.append("(");
for (int i = 0; i < signature.length; ++i)
sign.append(getDescriptorForType(signature[i].getName()));
sign.append(")" + getDescriptorForType(returnType));
return sign.toString();
}
public static String getDescriptorForType(String name)
{
if (name.equals(Byte.TYPE.getName())) return "B";
else if (name.equals(Character.TYPE.getName())) return "C";
else if (name.equals(Double.TYPE.getName())) return "D";
else if (name.equals(Float.TYPE.getName())) return "F";
else if (name.equals(Integer.TYPE.getName())) return "I";
else if (name.equals(Long.TYPE.getName())) return "J";
else if (name.equals(Short.TYPE.getName())) return "S";
else if (name.equals(Boolean.TYPE.getName())) return "Z";
else if (name.equals(Void.TYPE.getName())) return "V";
else if (name.startsWith("[")) return name.replace('.', '/');
else return "L" + name.replace('.', '/') + ";";
}
public static boolean isPrimitive(String name)
{
if (name.equals(Byte.TYPE.getName()) ||
name.equals(Character.TYPE.getName()) ||
name.equals(Double.TYPE.getName()) ||
name.equals(Float.TYPE.getName()) ||
name.equals(Integer.TYPE.getName()) ||
name.equals(Long.TYPE.getName()) ||
name.equals(Short.TYPE.getName()) ||
name.equals(Boolean.TYPE.getName()))
return true;
return false;
}
protected static MethodGen createConstructor(ConstantPoolGen cp, String className)
{
InstructionList constrInstructions = new InstructionList();
int constrRefIndex = cp.addMethodref(
SUPER_CLASS.getName(),
"<init>",
"(" + getDescriptorForType(MBeanInfo.class.getName())
+ getDescriptorForType(AttributeOperationResolver.class.getName())
+ getDescriptorForType(Object.class.getName())
+ ")V"
);
constrInstructions.append(new ALOAD(0)); constrInstructions.append(new ALOAD(1)); constrInstructions.append(new ALOAD(2)); constrInstructions.append(new ALOAD(3)); constrInstructions.append(new INVOKESPECIAL(constrRefIndex)); constrInstructions.append(new RETURN());
MethodGen constrMethod = new MethodGen(
Constants.ACC_PUBLIC,
Type.VOID,
new Type[] {
new ObjectType(MBeanInfo.class.getName()),
new ObjectType(AttributeOperationResolver.class.getName()),
new ObjectType(Object.class.getName()) },
new String[] { "info", "resolver", "resource" },
"<init>",
className, constrInstructions, cp
);
constrMethod.setMaxStack(4);
return constrMethod;
}
protected static MethodGen createInvoke(ConstantPoolGen cp, MBeanInfo info, String className, String resourceClassName)
{
InstructionList invokeInstructions = new InstructionList();
MethodEntry[] operations = getOperations(info);
for (int i = 0; i < operations.length; ++i) {
operations[i].nameIndexInCP = cp.addString(operations[i].getName());
operations[i].methodIndexInCP = cp.addMethodref(
resourceClassName,
operations[i].getName(),
operations[i].methodDescriptor
);
}
int invokeIndex = cp.addMethodref(
SUPER_CLASS.getName(),
"invoke",
"(" + getDescriptorForType(String.class.getName())
+ getDescriptorForType(Object[].class.getName())
+ getDescriptorForType(String[].class.getName())
+
")" + getDescriptorForType(Object.class.getName())
);
int getResourceObjectIndex = cp.addMethodref(
SUPER_CLASS.getName(),
"getResourceObject",
"()Ljava/lang/Object;"
);
int strEqualsIndex = cp.addMethodref(
String.class.getName(),
"equals",
"(Ljava/lang/Object;)Z"
);
InstructionHandle beginTryBlock = null;
InstructionHandle endTryBlock = null;
IFNULL ifOperationEqualsNull = new IFNULL(null);
IFEQ operationElseIfBranch = null;
if (operations.length > 0)
{
invokeInstructions.append(new ALOAD(1));
beginTryBlock =
invokeInstructions.append(ifOperationEqualsNull);
for (int i = 0; i < operations.length; ++i)
{
InstructionHandle jumpToNextElse =
invokeInstructions.append(new ALOAD(1)); invokeInstructions.append(new LDC(operations[i].nameIndexInCP)); invokeInstructions.append(new INVOKEVIRTUAL(strEqualsIndex));
if (operationElseIfBranch != null)
operationElseIfBranch.setTarget(jumpToNextElse);
operationElseIfBranch = new IFEQ(null);
invokeInstructions.append(operationElseIfBranch);
invokeInstructions.append(new ALOAD(0)); invokeInstructions.append(new INVOKEVIRTUAL(getResourceObjectIndex));
int x = cp.addClass(resourceClassName);
invokeInstructions.append(new CHECKCAST(x));
if (operations[i].getSignature().length > 0)
{
for (int arrayIndex = 0; arrayIndex < operations[i].getSignature().length; ++arrayIndex)
{
invokeInstructions.append(new ALOAD(2)); invokeInstructions.append(new PUSH(cp, arrayIndex)); invokeInstructions.append(new AALOAD());
String type = operations[i].getSignature() [arrayIndex].getName();
if (isPrimitive(type))
invokeInstructions.append(convertObjectToPrimitive(cp, type));
else
{
x = cp.addClass(type);
invokeInstructions.append(new CHECKCAST(x)); }
}
}
x = operations[i].methodIndexInCP;
invokeInstructions.append(new INVOKEVIRTUAL(x));
String type = operations[i].getReturnType();
if (isPrimitive(type))
{
invokeInstructions.append(convertPrimitiveToObject(cp, type)); invokeInstructions.append(new ARETURN()); }
else if (type.equals(Void.TYPE.getName()))
{
invokeInstructions.append(new ACONST_NULL()); invokeInstructions.append(new ARETURN()); }
else
{
invokeInstructions.append(new ARETURN()); }
}
}
InstructionHandle jumpToSuperInvoke =
invokeInstructions.append(new ALOAD(0)); invokeInstructions.append(new ALOAD(1)); invokeInstructions.append(new ALOAD(2)); invokeInstructions.append(new ALOAD(3)); invokeInstructions.append(new INVOKESPECIAL(invokeIndex)); invokeInstructions.append(new ARETURN());
ifOperationEqualsNull.setTarget(jumpToSuperInvoke);
if (operations.length > 0)
{
if (operationElseIfBranch != null)
operationElseIfBranch.setTarget(jumpToSuperInvoke);
beginTryBlock = beginTryBlock.getNext();
endTryBlock = jumpToSuperInvoke.getPrev();
}
InstructionHandle exceptionHandlerCode =
invokeInstructions.append(new ALOAD(0));
invokeInstructions.append(new ALOAD(1));
invokeInstructions.append(new ALOAD(2));
invokeInstructions.append(new ALOAD(3));
invokeInstructions.append(new INVOKESPECIAL(invokeIndex));
invokeInstructions.append(new ARETURN());
MethodGen invokeMethod = new MethodGen(
Constants.ACC_PUBLIC,
Type.OBJECT,
new Type[] {
Type.STRING,
new ArrayType(Object.class.getName(), 1),
new ArrayType(String.class.getName(), 1)
},
new String[] {
"operationName",
"args",
"signature"
},
"invoke",
className, invokeInstructions, cp
);
invokeMethod.setMaxLocals(7);
invokeMethod.setMaxStack(calculateMaxStackSize(info));
invokeMethod.addException(ReflectionException.class.getName());
invokeMethod.addException(MBeanException.class.getName());
if (operations.length > 0) {
invokeMethod.addExceptionHandler(beginTryBlock, endTryBlock, exceptionHandlerCode, new ObjectType("java.lang.Throwable"));
}
return invokeMethod;
}
private static int calculateMaxStackSize(MBeanInfo info)
{
MBeanOperationInfo[] operations = info.getOperations();
int maxSize = 7;
for (int i = 0; i < operations.length; ++i)
{
if (operations[i].getSignature().length > maxSize + 2)
maxSize = operations[i].getSignature().length + 2;
}
return maxSize;
}
protected static InstructionList convertObjectToPrimitive(ConstantPoolGen cp, String type)
{
InstructionList il = new InstructionList();
int intValueIndex = cp.addMethodref(Integer.class.getName(), "intValue", "()I");
int byteValueIndex = cp.addMethodref(Byte.class.getName(), "byteValue", "()B");
int charValueIndex = cp.addMethodref(Character.class.getName(), "charValue", "()C");
int doubleValueIndex = cp.addMethodref(Double.class.getName(), "doubleValue", "()D");
int floatValueIndex = cp.addMethodref(Float.class.getName(), "floatValue", "()F");
int longValueIndex = cp.addMethodref(Long.class.getName(), "longValue", "()J");
int shortValueIndex = cp.addMethodref(Short.class.getName(), "shortValue", "()S");
int booleanValueIndex = cp.addMethodref(Boolean.class.getName(), "booleanValue", "()Z");
if (type.equals(Integer.TYPE.getName()))
{
int x = cp.addClass("java.lang.Integer");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(intValueIndex)); }
else if (type.equals(Byte.TYPE.getName()))
{
int x = cp.addClass("java.lang.Byte");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(byteValueIndex)); }
else if (type.equals(Character.TYPE.getName()))
{
int x = cp.addClass("java.lang.Character");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(charValueIndex)); }
else if (type.equals(Double.TYPE.getName()))
{
int x = cp.addClass("java.lang.Double");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(doubleValueIndex)); }
else if (type.equals(Float.TYPE.getName()))
{
int x = cp.addClass("java.lang.Float");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(floatValueIndex)); }
else if (type.equals(Long.TYPE.getName()))
{
int x = cp.addClass("java.lang.Long");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(longValueIndex)); }
else if (type.equals(Short.TYPE.getName()))
{
int x = cp.addClass("java.lang.Short");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(shortValueIndex)); }
else if (type.equals(Boolean.TYPE.getName()))
{
int x = cp.addClass("java.lang.Boolean");
il.append(new CHECKCAST(x)); il.append(new INVOKEVIRTUAL(booleanValueIndex)); }
return il;
}
protected static InstructionList convertPrimitiveToObject(ConstantPoolGen cp, String type)
{
InstructionList il = new InstructionList();
if (type.equals(Boolean.TYPE.getName()))
{
int x = cp.addClass("java.lang.Boolean");
int constrIndex = cp.addMethodref("java.lang.Boolean", "<init>", "(B)V");
il.append(new ISTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new ILOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
else if (type.equals(Short.TYPE.getName()))
{
int x = cp.addClass("java.lang.Short");
int constrIndex = cp.addMethodref("java.lang.Short", "<init>", "(S)V");
il.append(new ISTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new ILOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
else if (type.equals(Long.TYPE.getName()))
{
int x = cp.addClass("java.lang.Long");
int constrIndex = cp.addMethodref("java.lang.Long", "<init>", "(J)V");
il.append(new LSTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(6));
il.append(new ALOAD(6));
il.append(new LLOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(6));
}
else if (type.equals(Integer.TYPE.getName()))
{
int x = cp.addClass("java.lang.Integer");
int constrIndex = cp.addMethodref("java.lang.Integer", "<init>", "(I)V");
il.append(new ISTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new ILOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
else if (type.equals(Float.TYPE.getName()))
{
int x = cp.addClass("java.lang.Float");
int constrIndex = cp.addMethodref("java.lang.Float", "<init>", "(F)V");
il.append(new FSTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new FLOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
else if (type.equals(Double.TYPE.getName()))
{
int x = cp.addClass("java.lang.Double");
int constrIndex = cp.addMethodref("java.lang.Double", "<init>", "(D)V");
il.append(new DSTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(6));
il.append(new ALOAD(6));
il.append(new DLOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(6));
}
else if (type.equals(Character.TYPE.getName()))
{
int x = cp.addClass("java.lang.Character");
int constrIndex = cp.addMethodref("java.lang.Character", "<init>", "(C)V");
il.append(new ISTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new ILOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
else if (type.equals(Byte.TYPE.getName()))
{
int x = cp.addClass("java.lang.Byte");
int constrIndex = cp.addMethodref("java.lang.Byte", "<init>", "(B)V");
il.append(new ISTORE(4));
il.append(new NEW(x));
il.append(new ASTORE(5));
il.append(new ALOAD(5));
il.append(new ILOAD(4));
il.append(new INVOKESPECIAL(constrIndex));
il.append(new ALOAD(5));
}
return il;
}
protected static MethodEntry[] getOperations(MBeanInfo info)
{
HashMap operationMap = new HashMap();
ArrayList overloadList = new ArrayList();
MBeanOperationInfo[] operations = info.getOperations();
for (int i = 0; i < operations.length; ++i)
{
String methodName = operations[i].getName();
if (operationMap.containsKey(methodName))
overloadList.add(methodName);
else
operationMap.put(methodName, new MethodEntry(operations[i]));
}
Iterator it = overloadList.iterator();
while (it.hasNext())
operationMap.remove(it.next());
return (MethodEntry[])operationMap.values().toArray(new MethodEntry[0]);
}
private static class MethodEntry extends MBeanOperationInfo {
private static final long serialVersionUID = 1792631947840418314L;
String methodDescriptor = null;
int nameIndexInCP = -1;
int methodIndexInCP = -1;
public MethodEntry(MBeanOperationInfo info)
{
super(info.getName(), info.getDescription(), info.getSignature(), info.getReturnType(), info.getImpact());
this.methodDescriptor = getMethodDescriptor(info.getSignature(), info.getReturnType());
}
}
}