/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.io.ByteArrayOutputStream; import java.io.BufferedOutputStream; import java.net.URL; import java.net.URLClassLoader; import javax.management.IntrospectionException; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.ReflectionException; import javax.management.loading.DefaultLoaderRepository; import org.apache.bcel.Constants; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.ArrayType; import org.apache.bcel.generic.Type; import org.apache.bcel.generic.ACONST_NULL; import org.apache.bcel.generic.ALOAD; import org.apache.bcel.generic.AALOAD; import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.ARETURN; import org.apache.bcel.generic.CHECKCAST; import org.apache.bcel.generic.DLOAD; import org.apache.bcel.generic.DSTORE; import org.apache.bcel.generic.FLOAD; import org.apache.bcel.generic.FSTORE; import org.apache.bcel.generic.ICONST; import org.apache.bcel.generic.IFEQ; import org.apache.bcel.generic.IFNULL; import org.apache.bcel.generic.IFNONNULL; import org.apache.bcel.generic.ILOAD; import org.apache.bcel.generic.IRETURN; import org.apache.bcel.generic.ISTORE; import org.apache.bcel.generic.INVOKESTATIC; import org.apache.bcel.generic.INVOKESPECIAL; import org.apache.bcel.generic.INVOKEVIRTUAL; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.LDC; import org.apache.bcel.generic.LLOAD; import org.apache.bcel.generic.LSTORE; import org.apache.bcel.generic.NEW; import org.apache.bcel.generic.POP; import org.apache.bcel.generic.PUSH; import org.apache.bcel.generic.RETURN; import org.apache.bcel.classfile.JavaClass; import org.jboss.mx.loading.LoaderRepository; import org.jboss.mx.loading.UnifiedClassLoader; import org.jboss.mx.server.ServerConstants; import org.jboss.mx.metadata.AttributeOperationResolver; /** * 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: 1.4.6.2 $ * */ 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.
* *
*
* 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(), "
*
* 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 [* * 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 { 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()); } } }