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

org.fabric3.implementation.bytecode.reflection.BytecodeConsumerInvokerFactory Maven / Gradle / Ivy

The newest version!
/*
 * Fabric3
 * Copyright (c) 2009-2013 Metaform Systems
 *
 * Fabric3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version, with the
 * following exception:
 *
 * Linking this software statically or dynamically with other
 * modules is making a combined work based on this software.
 * Thus, the terms and conditions of the GNU General Public
 * License cover the whole combination.
 *
 * As a special exception, the copyright holders of this software
 * give you permission to link this software with independent
 * modules to produce an executable, regardless of the license
 * terms of these independent modules, and to copy and distribute
 * the resulting executable under terms of your choice, provided
 * that you also meet, for each linked independent module, the
 * terms and conditions of the license of that module. An
 * independent module is a module which is not derived from or
 * based on this software. If you modify this software, you may
 * extend this exception to your version of the software, but
 * you are not obligated to do so. If you do not wish to do so,
 * delete this exception statement from your version.
 *
 * Fabric3 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 General Public License for more details.
 *
 * You should have received a copy of the
 * GNU General Public License along with Fabric3.
 * If not, see .
 *
 */
package org.fabric3.implementation.bytecode.reflection;

import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.fabric3.host.Names;
import org.fabric3.implementation.pojo.spi.reflection.ConsumerInvoker;
import org.fabric3.implementation.pojo.spi.reflection.ConsumerInvokerFactory;
import org.fabric3.implementation.pojo.spi.reflection.ServiceInvoker;
import org.fabric3.spi.builder.classloader.ClassLoaderListener;
import org.fabric3.spi.classloader.BytecodeClassLoader;
import org.fabric3.spi.classloader.ClassLoaderRegistry;
import org.fabric3.spi.classloader.MultiParentClassLoader;
import org.oasisopen.sca.annotation.Reference;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;

/**
 *
 */
public class BytecodeConsumerInvokerFactory implements ConsumerInvokerFactory, ClassLoaderListener {
    private static final String[] TARGET_INVOKER_INTERFACES = new String[]{Type.getInternalName(ConsumerInvoker.class)};
    private static final String[] EXCEPTIONS = new String[]{"java/lang/Exception"};

    private ClassLoaderRegistry classLoaderRegistry;

    private Map classLoaderCache = new HashMap();

    public BytecodeConsumerInvokerFactory(@Reference ClassLoaderRegistry classLoaderRegistry) {
        this.classLoaderRegistry = classLoaderRegistry;
    }

    public void onDeploy(ClassLoader classLoader) {
        // no-ip
    }

    public void onUndeploy(ClassLoader classLoader) {
        if (!(classLoader instanceof MultiParentClassLoader)) {
            return;
        }
        // remove cached classloader for the contribution on undeploy
        classLoaderCache.remove(((MultiParentClassLoader) classLoader).getName());
    }

    public boolean isDefault() {
        return false;
    }

    @SuppressWarnings("unchecked")
    public ConsumerInvoker createInvoker(Method method) {
        BytecodeClassLoader classLoader = getClassLoader(method);

        Class declaringClass = method.getDeclaringClass();

        // use the toString() hashcode of the method since more than one invoker may be created per class (if it has multiple methods)
        int code = Math.abs(method.toString().hashCode());
        String className = declaringClass.getName() + "_ConsumerInvoker" + code;

        try {
            Class invokerClass = (Class) classLoader.loadClass(className);
            return invokerClass.newInstance();
        } catch (ClassNotFoundException e) {
            // ignore
        } catch (InstantiationException e) {
            throw new AssertionError(e);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        }

        String internalTargetName = Type.getInternalName(declaringClass);
        String internalInvokerName = internalTargetName + "_ConsumerInvoker" + code;

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        cw.visit(Opcodes.V1_7, ACC_PUBLIC + ACC_SUPER, internalInvokerName, null, "java/lang/Object", TARGET_INVOKER_INTERFACES);

        cw.visitSource(className + ".java", null);

        // write the ctor
        BytecodeHelper.writeConstructor(cw, Object.class);

        // write the invoker method
        writeTargetInvoke(method, internalTargetName, cw);

        cw.visitEnd();

        return BytecodeHelper.instantiate(ConsumerInvoker.class, className, classLoader, cw);
    }

    private void writeTargetInvoke(Method method, String internalTargetName, ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, EXCEPTIONS);
        mv.visitCode();
        Label label1 = new Label();
        mv.visitLabel(label1);
        mv.visitLineNumber(9, label1);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitTypeInsn(Opcodes.CHECKCAST, internalTargetName);

        if (method.getParameterTypes().length == 1) {
            // single argument method, load the parameter passes on to the stack
            Class paramType = method.getParameterTypes()[0];
            mv.visitVarInsn(Opcodes.ALOAD, 2);

            writeParam(paramType, mv);

        } else if (method.getParameterTypes().length > 1) {
            // multi-argument method: cast the parameter to an object array and then load each element on the stack to be passed as params
            mv.visitVarInsn(Opcodes.ALOAD, 2);

            mv.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/Object;");
            mv.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/Object;");
            mv.visitVarInsn(Opcodes.ASTORE, 3);

            int pos = 0;
            mv.visitVarInsn(Opcodes.ALOAD, 3);
            for (Class paramType : method.getParameterTypes()) {
                mv.visitInsn(Opcodes.ICONST_0 + pos);
                mv.visitInsn(Opcodes.AALOAD);

                writeParam(paramType, mv);

                if (pos < method.getParameterTypes().length - 1) {
                    mv.visitVarInsn(Opcodes.ALOAD, 3);
                }
                pos++;
            }
        }

        // invoke the instance
        String methodName = method.getName();
        String methodDescriptor = Type.getMethodDescriptor(method);

        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, internalTargetName, methodName, methodDescriptor);

        Class returnType = method.getReturnType();
        writeReturn(returnType, mv);

        Label label2 = new Label();
        mv.visitLabel(label2);
        String descriptor = Type.getDescriptor(ServiceInvoker.class);

        mv.visitLocalVariable("this", descriptor, null, label1, label2, 0);
        mv.visitLocalVariable("instance", "Ljava/lang/Object;", null, label1, label2, 1);
        mv.visitLocalVariable("arg", "Ljava/lang/Object;", null, label1, label2, 2);
        mv.visitMaxs(2, 3);
        mv.visitEnd();
    }

    private void writeReturn(Class returnType, MethodVisitor mv) {
        if (Void.TYPE.equals(returnType)) {
            mv.visitInsn(Opcodes.ACONST_NULL);
            mv.visitInsn(Opcodes.ARETURN);
        } else if (returnType.isPrimitive()) {
            if (Integer.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Boolean.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Double.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Long.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Float.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Short.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
                mv.visitInsn(Opcodes.ARETURN);
            } else if (Byte.TYPE.equals(returnType)) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
                mv.visitInsn(Opcodes.ARETURN);
            }
        } else {
            mv.visitInsn(Opcodes.ARETURN);
        }
    }

    private void writeParam(Class paramType, MethodVisitor mv) {
        if (Integer.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
        } else if (Boolean.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        } else if (Double.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
        } else if (Float.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Float");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
        } else if (Short.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Short");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
        } else if (Byte.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Byte");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
        } else if (Long.TYPE.equals(paramType)) {
            mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
        } else {
            mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(paramType));
        }
    }

    /**
     * Returns a classloader for loading the proxy class, creating one if necessary.
     *
     * @return the classloader
     */
    private BytecodeClassLoader getClassLoader(Member method) {

        URI classLoaderKey;
        ClassLoader classLoader = method.getDeclaringClass().getClassLoader();
        if (classLoader instanceof MultiParentClassLoader) {
            classLoaderKey = ((MultiParentClassLoader) classLoader).getName();
        } else {
            classLoaderKey = Names.BOOT_CONTRIBUTION;
        }

        ClassLoader parent = classLoaderRegistry.getClassLoader(classLoaderKey);
        BytecodeClassLoader generationClassLoader = classLoaderCache.get(classLoaderKey);
        if (generationClassLoader == null) {
            generationClassLoader = new BytecodeClassLoader(classLoaderKey, parent);
            generationClassLoader.addParent(getClass().getClassLoader()); // SPI classes need to be visible as well
            classLoaderCache.put(classLoaderKey, generationClassLoader);
        }
        return generationClassLoader;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy