org.jboss.mx.capability.OptimizedMBeanDispatcher Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
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;
/**
* Byte code optimized dispatcher for Standard MBeans. This dispatcher generates
* an invoke implementation that handles the operation dispatching without
* Java reflection.
*
* The use of this dispatcher may be controlled by setting a
* {@link org.jboss.mx.server.ServerConstants#OPTIMIZE_REFLECTED_DISPATCHER OPTIMIZE_REFLECTED_DISPATCHER}
* property to either "true" or "false" string value.
*
* @see org.jboss.mx.capability.ReflectedMBeanDispatcher
* @see org.jboss.mx.capability.DispatchClassLoader
*
* @author Juha Lindfors.
* @version $Revision: 81026 $
*
*/
public class OptimizedMBeanDispatcher implements ServerConstants
{
// Constants -----------------------------------------------------
final static Class SUPER_CLASS = ReflectedMBeanDispatcher.class;
// Static --------------------------------------------------------
public static ReflectedMBeanDispatcher create(MBeanInfo info, Object resource)
{
try
{
// construct class template
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);
// FIXME: what about ctx cl?
// FIXME: also I dont know if the parent is right here, have to check later
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();
}
}
/**
* Returns the signature of a MBean operation using the grammar required by
* the class file format, excluding the method name.
*
*
The Java Virtual Machine Specification: 4.3.3 Method Descriptors
*
* A method descriptor represents the parameters that the method takes and the value that it returns:
*
* MethodDescriptor:
* ( ParameterDescriptor* ) ReturnDescriptor
*
*
* A parameter descriptor represents a parameter passed to a method:
*
* ParameterDescriptor:
* FieldType
*
*
* A return descriptor represents the type of the value returned from a method. It is a series of characters generated by the grammar:
*
* ReturnDescriptor:
* FieldType
* V
*
*
* The character V indicates that the method returns no value (its return type is void).
*
* For example, the method descriptor for the method
*
*
Object mymethod(int i, double d, Thread t)
*
* is
*
* (IDLjava/lang/Thread;)Ljava/lang/Object;
*
* Note that internal forms of the fully qualified names of Thread and Object are used in the method descriptor.
*/
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();
}
/**
* Returns a descriptor for a given Java type. See {@link java.lang.Class#getName() Class.getName()}
* for details on the grammar for arrays and primitive types. Note that the internal form of the fully
* qualified name for class Object is used, so for example, the returned descriptor for
* java.lang.Object is
*
* Ljava/lang/Object;
*
* See JVM spec 4.2 and 4.3 for detailed description of the internal class name format and grammar notation.
*
* @param name fully qualified name of the Java type
* @return descriptor string using the JVM grammar
*/
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('.', '/') + ";";
}
/**
* Checks if a given name matches the TYPE name of a primitive wrapper class.
*
* @see java.lang.Integer#TYPE
*
* @param name TYPE.getName()
* @return true if is a primitive type name; false otherwise
*/
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 -----------------------------------------------------
/**
* creates constructor <init>(MBeanInfo info, AttributeOperationResolver resolver, Object resource)
* that calls super(info, resolver, resource) in its implementation
*
* @param cp constant pool
* @param className name of the class being generated
*/
protected static MethodGen createConstructor(ConstantPoolGen cp, String className)
{
InstructionList constrInstructions = new InstructionList();
int constrRefIndex = cp.addMethodref(
SUPER_CLASS.getName(),
"",
"(" + getDescriptorForType(MBeanInfo.class.getName())
+ getDescriptorForType(AttributeOperationResolver.class.getName())
+ getDescriptorForType(Object.class.getName())
+ ")V"
);
constrInstructions.append(new ALOAD(0)); // Stack: => ..., this
constrInstructions.append(new ALOAD(1)); // Stack: => ..., this, arg1 [MBeanInfo]
constrInstructions.append(new ALOAD(2)); // Stack: => ..., this, arg1 [MBeanInfo], arg2 [AttributeOperationResolver]
constrInstructions.append(new ALOAD(3)); // Stack: => ..., this, arg1 [MBeanInfo], arg2 [AttributeOperationResolver], arg3 [Object]
constrInstructions.append(new INVOKESPECIAL(constrRefIndex)); // Stack: => ...
constrInstructions.append(new RETURN()); // Stack: =>
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" },
"",
className, constrInstructions, cp
);
constrMethod.setMaxStack(4);
return constrMethod;
}
/**
* Creates the implementation of the invoke(String actionName, Object[] args, String[] signature)
* method. This implementation currently handles all non overloaded MBean operation invocations.
* Overloaded operations still fall back to the default reflected invocations.
*
* The Java equivalent of the implementation looks roughly as follows:
*
* public void invoke(String actionName, Object[] args, String[] signature)
* {
* if (actionName != null)
* {
* try
* {
* if (actionName.equals(<operName1>))
* return ((<resource type>)super.getResourceObject()).<operName1>((<arg1 type>)arg1, (<arg2 type>)arg2, ...);
* else if (actionName.equals(<operName2>))
* return ((<resource type>)super.getResourceObject()).<operName2>((<arg1 type>)arg1, (<arg2 type>)arg2, ...);
*
* ...
*
* else
* super.invoke(actionName, args, signature);
* }
* catch (Throwable t)
* {
* super.invoke(actionName, args, signature);
* }
* }
* }
*
*
* @param cp constant pool of the class being generated
* @param info metadata of the MBean
* @param className name of the class being generated
* @param resourceClassName name of the resource class being invoked
*/
protected static MethodGen createInvoke(ConstantPoolGen cp, MBeanInfo info, String className, String resourceClassName)
{
InstructionList invokeInstructions = new InstructionList();
MethodEntry[] operations = getOperations(info);
// load operation name strings and methods to constant pool
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)
{
//
// if (actionName != null)
//
invokeInstructions.append(new ALOAD(1)); // Stack: => ..., arg1 [String]
beginTryBlock =
invokeInstructions.append(ifOperationEqualsNull); // Stack: => ...
for (int i = 0; i < operations.length; ++i)
{
//
// if (actionName.equals(operations[i].getName());
//
InstructionHandle jumpToNextElse =
invokeInstructions.append(new ALOAD(1)); // Stack: => ..., arg1 [String]
invokeInstructions.append(new LDC(operations[i].nameIndexInCP)); // Stack: => ..., opName [String]
invokeInstructions.append(new INVOKEVIRTUAL(strEqualsIndex)); // Stack: => ..., 0 | 1 [boolean]
// set the jump target for previous else if branch
if (operationElseIfBranch != null)
operationElseIfBranch.setTarget(jumpToNextElse);
operationElseIfBranch = new IFEQ(null);
invokeInstructions.append(operationElseIfBranch); // Stack: => ...
invokeInstructions.append(new ALOAD(0)); // Stack: => ..., this
invokeInstructions.append(new INVOKEVIRTUAL(getResourceObjectIndex)); // Stack: => ..., resource [Object]
int x = cp.addClass(resourceClassName);
invokeInstructions.append(new CHECKCAST(x)); // Stack: => ..., resource []
// if invocation has args, we need to push them into stack
if (operations[i].getSignature().length > 0)
{
for (int arrayIndex = 0; arrayIndex < operations[i].getSignature().length; ++arrayIndex)
{
invokeInstructions.append(new ALOAD(2)); // Stack: => ..., resource [], arg2 [Object[]]
invokeInstructions.append(new PUSH(cp, arrayIndex)); // Stack: => ..., resource [], arg2 [Object[]], array index [int]
invokeInstructions.append(new AALOAD()); // Stack: => ..., resource [], array[index] [Object]
// Args come in as objects. If signature has a primitive type
// we need to convert the arg before we can invoke the operation
String type = operations[i].getSignature() [arrayIndex].getName();
if (isPrimitive(type))
invokeInstructions.append(convertObjectToPrimitive(cp, type)); // Stack: => ..., resource[], value []
else
{
x = cp.addClass(type);
invokeInstructions.append(new CHECKCAST(x)); // Stack: => ..., resource[], value []
}
}
}
//
// resource.()
//
x = operations[i].methodIndexInCP;
invokeInstructions.append(new INVOKEVIRTUAL(x)); // Stack: => ..., returnvalue
// Wrap primitive return values into their corresponding wrapper objects
String type = operations[i].getReturnType();
if (isPrimitive(type))
{
invokeInstructions.append(convertPrimitiveToObject(cp, type)); // Stack: => ..., objectref [wrapper]
invokeInstructions.append(new ARETURN()); // Stack: =>
}
else if (type.equals(Void.TYPE.getName()))
{
invokeInstructions.append(new ACONST_NULL()); // Stack: => ..., null
invokeInstructions.append(new ARETURN()); // Stack: =>
}
else
{
invokeInstructions.append(new ARETURN()); // Stack: =>
}
}
}
//
// super.invoke(actionName, args, signature) if no match was found
//
InstructionHandle jumpToSuperInvoke =
invokeInstructions.append(new ALOAD(0)); // Stack: => ..., this
invokeInstructions.append(new ALOAD(1)); // Stack: => ..., this, arg1 [String]
invokeInstructions.append(new ALOAD(2)); // Stack: => ..., this, arg1 [String], arg2 [Object[]]
invokeInstructions.append(new ALOAD(3)); // Stack: => ..., this, arg1 [String], arg2 [Object[]], arg3 [String[]]
invokeInstructions.append(new INVOKESPECIAL(invokeIndex)); // Stack: => ..., reference [Object]
invokeInstructions.append(new ARETURN()); // Stack: =>
// set the jump targets
ifOperationEqualsNull.setTarget(jumpToSuperInvoke);
if (operations.length > 0)
{
// set the last else branch to call super.invoke
if (operationElseIfBranch != null)
operationElseIfBranch.setTarget(jumpToSuperInvoke);
// set the try catch block limits
beginTryBlock = beginTryBlock.getNext();
endTryBlock = jumpToSuperInvoke.getPrev();
}
// exception handler (it's a cheap shot -- if there is any exception, re-invoke
// on super class and let it handle all exceptions)
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;
}
/**
* Converts a reference of a primitve wrapper object into a primite value type.
* This method assumes that the wrapper object reference is already loaded at the
* top of the operand stack. The stack is modified so that the object reference
* to a primitive wrapper is replaced by the corresponding value in the stack.
*
* @param cp constant pool
* @param type class name of the primitive wrapper object to convert
* @return an instruction list that replaces an object reference of a primitive
* wrapper object to its corresponding value in the operand stack
*/
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");
//
// Assumes the wrapper object reference is on top of the stack
//
if (type.equals(Integer.TYPE.getName()))
{
int x = cp.addClass("java.lang.Integer");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Integer]
il.append(new INVOKEVIRTUAL(intValueIndex)); // Stack: => ..., value [int]
}
else if (type.equals(Byte.TYPE.getName()))
{
int x = cp.addClass("java.lang.Byte");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Boolean]
il.append(new INVOKEVIRTUAL(byteValueIndex)); // Stack: => ..., 0 | 1 [boolean]
}
else if (type.equals(Character.TYPE.getName()))
{
int x = cp.addClass("java.lang.Character");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Character]
il.append(new INVOKEVIRTUAL(charValueIndex)); // Stack: => ..., value [char]
}
else if (type.equals(Double.TYPE.getName()))
{
int x = cp.addClass("java.lang.Double");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Double]
il.append(new INVOKEVIRTUAL(doubleValueIndex)); // Stack: => ..., value [double]
}
else if (type.equals(Float.TYPE.getName()))
{
int x = cp.addClass("java.lang.Float");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Float]
il.append(new INVOKEVIRTUAL(floatValueIndex)); // Stack: => ..., value [float]
}
else if (type.equals(Long.TYPE.getName()))
{
int x = cp.addClass("java.lang.Long");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Long]
il.append(new INVOKEVIRTUAL(longValueIndex)); // Stack: => ..., value [long]
}
else if (type.equals(Short.TYPE.getName()))
{
int x = cp.addClass("java.lang.Short");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Short]
il.append(new INVOKEVIRTUAL(shortValueIndex)); // Stack: => ..., value [short]
}
else if (type.equals(Boolean.TYPE.getName()))
{
int x = cp.addClass("java.lang.Boolean");
il.append(new CHECKCAST(x)); // Stack: => ..., type [Boolean]
il.append(new INVOKEVIRTUAL(booleanValueIndex)); // Stack: => ..., value [boolean]
}
return il;
}
/**
* Converts a primitive into its corresponding object wrapper reference.
* This method assumes the primitve is already pushed to the top of the operand
* stack. The stack is modified so that the primitive value is replaced
* by a reference to its corresponding wrapper object that has been
* initialized to contain the same value.
*
* @param cp constant pool
* @param type type string of the primitive, for example {@link java.lang.Integer#TYPE Integer.TYPE.getName()}
* @return an instruction list that replaces the primitive type at the top of
* the operand stack with its corresponding, initialized, wrapper object
*/
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", "", "(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", "", "(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", "", "(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", "", "(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", "", "(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", "", "(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", "", "(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", "", "(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;
}
/**
* Returns a subset of MBean's operations. Overloaded operations are not supported yet,
* so they're left out of the list and their invocations are delegated to the reflection
* based super class.
*
* Overloaded operations that differ in their arg list length may be able to gain in
* performance if implemented directly with byte code. Overloaded operations with
* equal arg list length may not show much difference compared to ternary search tree
* based resolver.
*/
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]));
}
// method overloading not supported yet
Iterator it = overloadList.iterator();
while (it.hasNext())
operationMap.remove(it.next());
return (MethodEntry[])operationMap.values().toArray(new MethodEntry[0]);
}
// Inner classes -------------------------------------------------
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());
}
}
}