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

org.jboss.weld.bean.proxy.ProxyFactory Maven / Gradle / Ivy

There is a newer version: 6.0.0.Beta4
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2008, Red Hat, Inc. and/or its affiliates, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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.jboss.weld.bean.proxy;

import static org.jboss.weld.logging.Category.BEAN;
import static org.jboss.weld.logging.LoggerFactory.loggerFactory;
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_INSTANTIATION_BEAN_ACCESS_FAILED;
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_INSTANTIATION_FAILED;
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_SKIP_PACKAGE_PRIVATE_INTERFACE;
import static org.jboss.weld.util.reflection.Reflections.cast;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.DuplicateMemberException;
import javassist.bytecode.ExceptionTable;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;

import javax.enterprise.inject.spi.Bean;

import org.jboss.weld.Container;
import org.jboss.weld.exceptions.DefinitionException;
import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.interceptor.proxy.LifecycleMixin;
import org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy;
import org.jboss.weld.serialization.spi.ContextualStore;
import org.jboss.weld.serialization.spi.ProxyServices;
import org.jboss.weld.util.Proxies.TypeInfo;
import org.jboss.weld.util.bytecode.Boxing;
import org.jboss.weld.util.bytecode.BytecodeUtils;
import org.jboss.weld.util.bytecode.ClassFileUtils;
import org.jboss.weld.util.bytecode.ConstructorUtils;
import org.jboss.weld.util.bytecode.DescriptorUtils;
import org.jboss.weld.util.bytecode.JumpMarker;
import org.jboss.weld.util.bytecode.JumpUtils;
import org.jboss.weld.util.bytecode.MethodInformation;
import org.jboss.weld.util.bytecode.MethodUtils;
import org.jboss.weld.util.bytecode.RuntimeMethodInformation;
import org.jboss.weld.util.collections.ArraySet;
import org.jboss.weld.util.reflection.Reflections;
import org.jboss.weld.util.reflection.SecureReflections;
import org.jboss.weld.util.reflection.instantiation.InstantiatorFactory;
import org.slf4j.cal10n.LocLogger;

/**
 * Main factory to produce proxy classes and instances for Weld beans. This
 * implementation creates proxies which forward non-static method invocations to
 * a {@link BeanInstance}. All proxies implement the {@link Proxy} interface.
 *
 * @author David Allen
 * @author Stuart Douglas
 * @author Marius Bogoevici
 * @author Ales Justin
 */
public class ProxyFactory {
    // The log provider
    protected static final LocLogger log = loggerFactory().getLogger(BEAN);
    // Default proxy class name suffix
    public static final String PROXY_SUFFIX = "$Proxy$";
    public static final String DEFAULT_PROXY_PACKAGE = "org.jboss.weld.proxies";

    private final Class beanType;
    private final Set> additionalInterfaces = new LinkedHashSet>();
    private final ClassLoader classLoader;
    private final String baseProxyName;
    private final Bean bean;
    private final Class proxiedBeanType;

    public static final String CONSTRUCTED_FLAG_NAME = "constructed";

    protected static final BytecodeMethodResolver DEFAULT_METHOD_RESOLVER = new DefaultBytecodeMethodResolver();

    /**
     * created a new proxy factory from a bean instance. The proxy name is
     * generated from the bean id
     */
    public ProxyFactory(Class proxiedBeanType, Set typeClosure, Bean bean) {
        this(proxiedBeanType, typeClosure, getProxyName(proxiedBeanType, typeClosure, bean), bean);
    }

    /**
     * Creates a new proxy factory when the name of the proxy class is already
     * known, such as during de-serialization
     *
     * @param proxiedBeanType the super-class for this proxy class
     * @param typeClosure     the bean types of the bean
     * @param proxyName       the name of the proxy class
     */
    public ProxyFactory(Class proxiedBeanType, Set typeClosure, String proxyName, Bean bean) {
        this.bean = bean;
        this.proxiedBeanType = proxiedBeanType;
        addInterfacesFromTypeClosure(typeClosure, proxiedBeanType, proxyName);
        TypeInfo typeInfo = TypeInfo.of(typeClosure);
        Class superClass = typeInfo.getSuperClass();
        superClass = superClass == null ? Object.class : superClass;
        if (superClass.equals(Object.class) && additionalInterfaces.isEmpty()) {
            // No interface beans must use the bean impl as superclass
            superClass = proxiedBeanType;
        }
        this.beanType = superClass;
        addDefaultAdditionalInterfaces();
        baseProxyName = proxyName;
        this.classLoader = resolveClassLoaderForBeanProxy(bean, typeInfo);
        // hierarchy order
        List> list = new ArrayList>(additionalInterfaces);
        Collections.sort(list, ClassHierarchyComparator.INSTANCE);
        additionalInterfaces.clear();
        additionalInterfaces.addAll(list);
    }

    static String getProxyName(Class proxiedBeanType, Set typeClosure, Bean bean) {
        TypeInfo typeInfo = TypeInfo.of(typeClosure);
        String proxyPackage;
        if (proxiedBeanType.equals(Object.class)) {
            Class superInterface = typeInfo.getSuperInterface();
            if (superInterface == null) {
                throw new IllegalArgumentException("Proxied bean type cannot be java.lang.Object without an interface");
            } else {
                proxyPackage = DEFAULT_PROXY_PACKAGE;
            }
        } else {
            if (proxiedBeanType.getPackage() == null) {
                proxyPackage = DEFAULT_PROXY_PACKAGE;
            } else {
                proxyPackage = proxiedBeanType.getPackage().getName();
            }
        }
        final String className;

        if (typeInfo.getSuperClass() == Object.class) {
            final StringBuilder name = new StringBuilder();
            //interface only bean.
            className = createCompoundProxyName(bean, typeInfo, name) + PROXY_SUFFIX;
        } else {
            boolean typeModified = false;
            for (Class iface : typeInfo.getInterfaces()) {
                if (!iface.isAssignableFrom(typeInfo.getSuperClass())) {
                    typeModified = true;
                    break;
                }
            }
            if (typeModified) {
                //this bean has interfaces that the base type is not assignable to
                //which can happen with some creative use of the SPI
                //interface only bean.
                StringBuilder name = new StringBuilder(typeInfo.getSuperClass().getSimpleName() + "$");
                className = createCompoundProxyName(bean, typeInfo, name) + PROXY_SUFFIX;
            } else {
                className = typeInfo.getSuperClass().getSimpleName() + PROXY_SUFFIX;
            }
        }


        return proxyPackage + '.' + getEnclosingPrefix(proxiedBeanType) + className;
    }

    private static String createCompoundProxyName(Bean bean, TypeInfo typeInfo, StringBuilder name) {
        String className;
        final List interfaces = new ArrayList();
        for (Class type : typeInfo.getInterfaces()) {
            interfaces.add(type.getSimpleName());
        }
        Collections.sort(interfaces);
        for (final String iface : interfaces) {
            name.append(iface);
            name.append('$');
        }
        //there is a remote chance that this could generate the same
        //proxy name for two interfaces with the same simple name.
        //append the hash code of the bean id to be sure
        final String id = Container.instance().services().get(ContextualStore.class).putIfAbsent(bean);
        int idHash = id.hashCode();
        name.append(Math.abs(idHash == Integer.MIN_VALUE ? 0 : idHash));
        className = name.toString();
        return className;
    }

    private static String getEnclosingPrefix(Class clazz) {
        Class encl = clazz.getEnclosingClass();
        return encl == null ? "" : getEnclosingPrefix(encl) + encl.getSimpleName() + '$';
    }

    /**
     * Adds an additional interface that the proxy should implement. The default
     * implementation will be to forward invocations to the bean instance.
     *
     * @param newInterface an interface
     */
    public void addInterface(Class newInterface) {
        if (!newInterface.isInterface()) {
            throw new IllegalArgumentException(newInterface + " is not an interface");
        }
        additionalInterfaces.add(newInterface);
    }

    /**
     * Method to create a new proxy that wraps the bean instance.
     *
     * @param beanInstance the bean instance
     * @return a new proxy object
     */
    public T create(BeanInstance beanInstance) {
        T proxy;
        Class proxyClass = getProxyClass();
        try {
            InstantiatorFactory factory = Container.instance().services().get(InstantiatorFactory.class);
            if (factory != null && factory.useInstantiators()) {
                proxy = SecureReflections.newUnsafeInstance(proxyClass);
            } else {
                proxy = SecureReflections.newInstance(proxyClass);
            }
        } catch (InstantiationException e) {
            throw new DefinitionException(PROXY_INSTANTIATION_FAILED, e, this);
        } catch (IllegalAccessException e) {
            throw new DefinitionException(PROXY_INSTANTIATION_BEAN_ACCESS_FAILED, e, this);
        }
        ((ProxyObject) proxy).setHandler(new ProxyMethodHandler(beanInstance, bean));
        return proxy;
    }

    /**
     * Produces or returns the existing proxy class.
     *
     * @return always the class of the proxy
     */
    public Class getProxyClass() {
        String suffix = "_$$_Weld" + getProxyNameSuffix();
        String proxyClassName = getBaseProxyName();
        if (!proxyClassName.endsWith(suffix)) {
            proxyClassName = proxyClassName + suffix;
        }
        if (proxyClassName.startsWith("java")) {
            proxyClassName = proxyClassName.replaceFirst("java", "org.jboss.weld");
        }
        Class proxyClass = null;
        log.trace("Retrieving/generating proxy class " + proxyClassName);
        try {
            // First check to see if we already have this proxy class
            proxyClass = cast(classLoader.loadClass(proxyClassName));
        } catch (ClassNotFoundException e) {
            // Create the proxy class for this instance
            try {
                proxyClass = createProxyClass(proxyClassName);
            } catch (Throwable e1) {
                //attempt to load the class again, just in case another thread
                //defined it between the check and the create method
                try {
                    proxyClass = cast(classLoader.loadClass(proxyClassName));
                } catch (ClassNotFoundException e2) {
                    throw new WeldException(e1);
                }
            }
        }
        return proxyClass;
    }

    /**
     * Returns the package and base name for the proxy class.
     *
     * @return base name without suffixes
     */
    protected String getBaseProxyName() {
        return baseProxyName;
    }

    /**
     * Convenience method to determine if an object is a proxy generated by this
     * factory or any derived factory.
     *
     * @param proxySuspect the object suspected of being a proxy
     * @return true only if it is a proxy object
     */
    public static boolean isProxy(Object proxySuspect) {
        return proxySuspect instanceof ProxyObject;
    }

    /**
     * Convenience method to set the underlying bean instance for a proxy.
     *
     * @param proxy        the proxy instance
     * @param beanInstance the instance of the bean
     */
    public static  void setBeanInstance(T proxy, BeanInstance beanInstance, Bean bean) {
        if (proxy instanceof ProxyObject) {
            ProxyObject proxyView = (ProxyObject) proxy;
            proxyView.setHandler(new ProxyMethodHandler(beanInstance, bean));
        }
    }

    /**
     * Returns a suffix to append to the name of the proxy class. The name
     * already consists of _$$_Weld, to which the suffix is added.
     * This allows the creation of different types of proxies for the same class.
     *
     * @return a name suffix
     */
    protected String getProxyNameSuffix() {
        return PROXY_SUFFIX;
    }

    private void addDefaultAdditionalInterfaces() {
        additionalInterfaces.add(Serializable.class);
    }

    /**
     * Sub classes may override to specify additional interfaces the proxy should
     * implement
     */
    protected void addAdditionalInterfaces(Set> interfaces) {

    }

    private Class createProxyClass(String proxyClassName) throws Exception {
        ArraySet> specialInterfaces = new ArraySet>(3);
        specialInterfaces.add(LifecycleMixin.class);
        specialInterfaces.add(TargetInstanceProxy.class);
        specialInterfaces.add(ProxyObject.class);
        addAdditionalInterfaces(specialInterfaces);
        // Remove special interfaces from main set (deserialization scenario)
        additionalInterfaces.removeAll(specialInterfaces);

        ClassFile proxyClassType = null;
        if (beanType.isInterface()) {
            proxyClassType = new ClassFile(false, proxyClassName, Object.class.getName());
            proxyClassType.addInterface(beanType.getName());
        } else {
            proxyClassType = new ClassFile(false, proxyClassName, beanType.getName());
        }
        proxyClassType.setVersionToJava5();
        proxyClassType.setAccessFlags(AccessFlag.PUBLIC);
        // Add interfaces which require method generation
        for (Class clazz : additionalInterfaces) {
            proxyClassType.addInterface(clazz.getName());
        }
        Bytecode initialValueBytecode = new Bytecode(proxyClassType.getConstPool());

        addFields(proxyClassType, initialValueBytecode);
        addConstructors(proxyClassType, initialValueBytecode);
        addMethods(proxyClassType);

        // Additional interfaces whose methods require special handling
        for (Class specialInterface : specialInterfaces) {
            proxyClassType.addInterface(specialInterface.getName());
        }
        // TODO: change the ProxyServices SPI to allow the container to figure out
        // which PD to use


        ProtectionDomain domain = proxiedBeanType.getProtectionDomain();
        if (proxiedBeanType.getPackage() == null || proxiedBeanType.equals(Object.class)) {
            domain = ProxyFactory.class.getProtectionDomain();
        }
        Class proxyClass = cast(ClassFileUtils.toClass(proxyClassType, classLoader, domain));
        log.trace("Created Proxy class of type " + proxyClass + " supporting interfaces " + Arrays.toString(proxyClass.getInterfaces()));
        return proxyClass;
    }

    /**
     * Adds a constructor for the proxy for each constructor declared by the base
     * bean type.
     *
     * @param proxyClassType       the Javassist class for the proxy
     * @param initialValueBytecode
     */
    protected void addConstructors(ClassFile proxyClassType, Bytecode initialValueBytecode) {
        try {
            if (beanType.isInterface()) {
                ConstructorUtils.addDefaultConstructor(proxyClassType, initialValueBytecode);
            } else {
                boolean constructorFound = false;
                for (Constructor constructor : beanType.getDeclaredConstructors()) {
                    if ((constructor.getModifiers() & Modifier.PRIVATE) == 0) {
                        constructorFound = true;
                        String[] exceptions = new String[constructor.getExceptionTypes().length];
                        for (int i = 0; i < exceptions.length; ++i) {
                            exceptions[i] = constructor.getExceptionTypes()[i].getName();
                        }
                        ConstructorUtils.addConstructor(DescriptorUtils.getConstructorDescriptor(constructor), exceptions, proxyClassType, initialValueBytecode);
                    }
                }
                if (!constructorFound) {
                    // the bean only has private constructors, we need to generate
                    // two fake constructors that call each other
                    addConstructorsForBeanWithPrivateConstructors(proxyClassType);
                }
            }
        } catch (Exception e) {
            throw new WeldException(e);
        }
    }

    protected void addFields(ClassFile proxyClassType, Bytecode initialValueBytecode) {
        try {
            // The field representing the underlying instance or special method
            // handling
            proxyClassType.addField(new FieldInfo(proxyClassType.getConstPool(), "methodHandler", "Ljavassist/util/proxy/MethodHandler;"));
            // field used to indicate that super() has been called
            FieldInfo constfield = new FieldInfo(proxyClassType.getConstPool(), CONSTRUCTED_FLAG_NAME, "Z");
            constfield.setAccessFlags(AccessFlag.PRIVATE);
            proxyClassType.addField(constfield);
        } catch (Exception e) {
            throw new WeldException(e);
        }
    }

    protected void addMethods(ClassFile proxyClassType) {
        // Add all class methods for interception
        addMethodsFromClass(proxyClassType);

        // Add special proxy methods
        addSpecialMethods(proxyClassType);

        // Add serialization support methods
        addSerializationSupport(proxyClassType);
    }

    /**
     * Adds special serialization code. By default this is a nop
     *
     * @param proxyClassType the Javassist class for the proxy class
     */
    protected void addSerializationSupport(ClassFile proxyClassType) {
        //noop
    }

    protected void addMethodsFromClass(ClassFile proxyClassType) {
        try {
            // Add all methods from the class heirachy
            Class cls = beanType;
            // first add equals/hashCode methods if required
            MethodInfo equalsMethod = generateEqualsMethod(proxyClassType);
            if (equalsMethod != null) {
                proxyClassType.addMethod(equalsMethod);
            }
            MethodInfo hashCodeMethod = generateHashCodeMethod(proxyClassType);
            if (hashCodeMethod != null) {
                proxyClassType.addMethod(hashCodeMethod);
            }

            while (cls != null) {
                for (Method method : cls.getDeclaredMethods()) {
                    if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isFinal(method.getModifiers()) && (method.getDeclaringClass() != Object.class || method.getName().equals("toString"))) {
                        try {
                            MethodInformation methodInfo = new RuntimeMethodInformation(method);
                            proxyClassType.addMethod(MethodUtils.makeMethod(methodInfo, method.getExceptionTypes(), addConstructedGuardToMethodBody(proxyClassType, createForwardingMethodBody(proxyClassType, methodInfo), methodInfo), proxyClassType.getConstPool()));
                            log.trace("Adding method " + method);
                        } catch (DuplicateMemberException e) {
                            // do nothing. This will happen if superclass methods
                            // have been overridden
                        }
                    }
                }
                cls = cls.getSuperclass();
            }
            for (Class c : additionalInterfaces) {
                for (Method method : c.getMethods()) {
                    try {
                        MethodInformation methodInfo = new RuntimeMethodInformation(method);
                        proxyClassType.addMethod(MethodUtils.makeMethod(methodInfo, method.getExceptionTypes(), createSpecialMethodBody(proxyClassType, methodInfo), proxyClassType.getConstPool()));
                        log.trace("Adding method " + method);
                    } catch (DuplicateMemberException e) {
                    }
                }
            }
        } catch (Exception e) {
            throw new WeldException(e);
        }
    }

    /**
     * Generate the body of the proxies hashCode method.
     * 

* If this method returns null, the method will not be added, and the * hashCode on the superclass will be used as per normal virtual method * resolution rules */ protected MethodInfo generateHashCodeMethod(ClassFile proxyClassType) { return null; } /** * Generate the body of the proxies equals method. *

* If this method returns null, the method will not be added, and the * hashCode on the superclass will be used as per normal virtual method * resolution rules * * @param proxyClassType The class file */ protected MethodInfo generateEqualsMethod(ClassFile proxyClassType) { return null; } protected Bytecode createSpecialMethodBody(ClassFile proxyClassType, MethodInformation method) throws NotFoundException { return createInterceptorBody(proxyClassType, method); } /** * Adds the following code to a delegating method: *

* * if(!this.constructed) return super.thisMethod() * *

* This means that the proxy will not start to delegate to the underlying * bean instance until after the constructor has finished. */ protected Bytecode addConstructedGuardToMethodBody(ClassFile proxyClassType, Bytecode existingMethod, MethodInformation method) { String methodDescriptor = method.getDescriptor(); // now create the conditional Bytecode cond = new Bytecode(proxyClassType.getConstPool()); cond.add(Opcode.ALOAD_0); cond.addGetfield(proxyClassType.getName(), CONSTRUCTED_FLAG_NAME, "Z"); // jump if the proxy constructor has finished cond.add(Opcode.IFNE); JumpMarker invokeSpecial = JumpUtils.addJumpInstruction(cond); // generate the invokespecial call to the super class method // this is run when the proxy is being constructed cond.add(Opcode.ALOAD_0); BytecodeUtils.loadParameters(cond, methodDescriptor); cond.addInvokespecial(proxyClassType.getSuperclass(), method.getName(), methodDescriptor); BytecodeUtils.addReturnInstruction(cond, method.getReturnType()); invokeSpecial.mark(); // store the offset for copying the exception table int offset = cond.currentPc(); // copy the byecode of the original method byte[] methodBodyBytes = existingMethod.get(); for (byte methodBodyByte : methodBodyBytes) { cond.add(methodBodyByte); } // copy the exception table of the original method, if any (shift the table values to account for the guard) ExceptionTable originalExceptionTable = existingMethod.getExceptionTable(); if (originalExceptionTable.size() > 0) { for (int i = 0; i < originalExceptionTable.size(); i++) { cond.addExceptionHandler(originalExceptionTable.startPc(i) + offset, originalExceptionTable.endPc(i) + offset, originalExceptionTable.handlerPc(i) + offset, originalExceptionTable.catchType(i)); } } cond.setMaxLocals(existingMethod.getMaxLocals()); cond.setMaxStack(existingMethod.getMaxStack()); return cond; } protected Bytecode createForwardingMethodBody(ClassFile proxyClassType, MethodInformation method) throws NotFoundException { return createInterceptorBody(proxyClassType, method); } /** * Creates the given method on the proxy class where the implementation * forwards the call directly to the method handler. *

* the generated bytecode is equivalent to: *

* return (RetType) methodHandler.invoke(this,param1,param2); * * @param file the class file * @param method any JLR method * @return the method byte code */ protected Bytecode createInterceptorBody(ClassFile file, MethodInformation method) throws NotFoundException { Bytecode b = new Bytecode(file.getConstPool()); invokeMethodHandler(file, b, method, true, DEFAULT_METHOD_RESOLVER); return b; } /** * calls methodHandler.invoke for a given method * * @param file the current class file * @param b the bytecode to add the methodHandler.invoke call to * @param method The method information * @param addReturnInstruction set to true you want to return the result of * the method invocation * @param bytecodeMethodResolver The resolver that returns the method to invoke */ protected static void invokeMethodHandler(ClassFile file, Bytecode b, MethodInformation method, boolean addReturnInstruction, BytecodeMethodResolver bytecodeMethodResolver) { // now we need to build the bytecode. The order we do this in is as // follows: // load methodHandler // load this // load the method object // load null // create a new array the same size as the number of parameters // push our parameter values into the array // invokeinterface the invoke method // add checkcast to cast the result to the return type, or unbox if // primitive // add an appropriate return instruction b.add(Opcode.ALOAD_0); b.addGetfield(file.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); b.add(Opcode.ALOAD_0); bytecodeMethodResolver.getDeclaredMethod(file, b, method.getDeclaringClass(), method.getName(), method.getParameterTypes()); b.add(Opcode.ACONST_NULL); b.addIconst(method.getParameterTypes().length); b.addAnewarray("java.lang.Object"); int localVariableCount = 1; for (int i = 0; i < method.getParameterTypes().length; ++i) { String typeString = method.getParameterTypes()[i]; b.add(Opcode.DUP); // duplicate the array reference b.addIconst(i); // load the parameter value BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount); // box the parameter if nessesary Boxing.boxIfNessesary(b, typeString); // and store it in the array b.add(Opcode.AASTORE); if (DescriptorUtils.isWide(typeString)) { localVariableCount = localVariableCount + 2; } else { localVariableCount++; } } // now we have all our arguments on the stack // lets invoke the method b.addInvokeinterface(MethodHandler.class.getName(), "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", 5); if (addReturnInstruction) { // now we need to return the appropriate type if (method.getReturnType().equals("V")) { b.add(Opcode.RETURN); } else if (DescriptorUtils.isPrimitive(method.getReturnType())) { Boxing.unbox(b, method.getReturnType()); if (method.getReturnType().equals("D")) { b.add(Opcode.DRETURN); } else if (method.getReturnType().equals("F")) { b.add(Opcode.FRETURN); } else if (method.getReturnType().equals("J")) { b.add(Opcode.LRETURN); } else { b.add(Opcode.IRETURN); } } else { String castType = method.getReturnType(); if (!method.getReturnType().startsWith("[")) { castType = method.getReturnType().substring(1).substring(0, method.getReturnType().length() - 2); } b.addCheckcast(castType); b.add(Opcode.ARETURN); } if (b.getMaxLocals() < localVariableCount) { b.setMaxLocals(localVariableCount); } } } /** * Adds methods requiring special implementations rather than just * delegation. * * @param proxyClassType the Javassist class description for the proxy type */ protected void addSpecialMethods(ClassFile proxyClassType) { try { // Add special methods for interceptors for (Method method : LifecycleMixin.class.getDeclaredMethods()) { log.trace("Adding method " + method); MethodInformation methodInfo = new RuntimeMethodInformation(method); proxyClassType.addMethod(MethodUtils.makeMethod(methodInfo, method.getExceptionTypes(), createInterceptorBody(proxyClassType, methodInfo), proxyClassType.getConstPool())); } Method getInstanceMethod = TargetInstanceProxy.class.getDeclaredMethod("getTargetInstance"); Method getInstanceClassMethod = TargetInstanceProxy.class.getDeclaredMethod("getTargetClass"); MethodInformation getInstanceMethodInfo = new RuntimeMethodInformation(getInstanceMethod); proxyClassType.addMethod(MethodUtils.makeMethod(getInstanceMethodInfo, getInstanceMethod.getExceptionTypes(), createInterceptorBody(proxyClassType, getInstanceMethodInfo), proxyClassType.getConstPool())); MethodInformation getInstanceClassMethodInfo = new RuntimeMethodInformation(getInstanceClassMethod); proxyClassType.addMethod(MethodUtils.makeMethod(getInstanceClassMethodInfo, getInstanceClassMethod.getExceptionTypes(), createInterceptorBody(proxyClassType, getInstanceClassMethodInfo), proxyClassType.getConstPool())); Method setMethodHandlerMethod = ProxyObject.class.getDeclaredMethod("setHandler", MethodHandler.class); MethodInformation setMethodHandlerMethodInfo = new RuntimeMethodInformation(setMethodHandlerMethod); proxyClassType.addMethod(MethodUtils.makeMethod(setMethodHandlerMethodInfo, setMethodHandlerMethod.getExceptionTypes(), generateSetMethodHandlerBody(proxyClassType), proxyClassType.getConstPool())); } catch (Exception e) { throw new WeldException(e); } } protected void addInterfacesFromTypeClosure(Set typeClosure, Class proxiedBeanType, String proxyName) { String proxyPackage = proxyName.substring(0, proxyName.lastIndexOf(".")); for (Type type : typeClosure) { Class c = Reflections.getRawType(type); // Ignore no-interface views, they are dealt with proxiedBeanType // (pending redesign) if (c.isInterface()) { if (isNonAccessiblePackagePrivateInterface(c, proxyPackage)) { // Skip non-accessible package-private interfaces otherwise proxy creation will fail log.debug(PROXY_SKIP_PACKAGE_PRIVATE_INTERFACE, c.getName(), proxiedBeanType.getName()); continue; } addInterface(c); } } } private boolean isNonAccessiblePackagePrivateInterface(Class clazz, String proxyPackage) { return Reflections.isPackagePrivate(clazz.getModifiers()) && (clazz.getPackage() == null || !proxyPackage.equals(clazz.getPackage().getName())); } private static Bytecode generateSetMethodHandlerBody(ClassFile file) { Bytecode b = new Bytecode(file.getConstPool(), 3, 2); b.add(Opcode.ALOAD_0); b.add(Opcode.ALOAD_1); b.addPutfield(file.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); b.add(Opcode.RETURN); return b; } /** * Adds two constructors to the class that call each other in order to bypass * the JVM class file verifier. *

* This would result in a stack overflow if they were actually called, * however the proxy is directly created without calling the constructor */ private void addConstructorsForBeanWithPrivateConstructors(ClassFile proxyClassType) { try { MethodInfo ctor = new MethodInfo(proxyClassType.getConstPool(), "", "(Ljava/lang/Byte;)V"); Bytecode b = new Bytecode(proxyClassType.getConstPool(), 3, 3); b.add(Opcode.ALOAD_0); b.add(Opcode.ACONST_NULL); b.add(Opcode.ACONST_NULL); b.addInvokespecial(proxyClassType.getName(), "", "(Ljava/lang/Byte;Ljava/lang/Byte;)V"); b.add(Opcode.RETURN); ctor.setCodeAttribute(b.toCodeAttribute()); ctor.setAccessFlags(AccessFlag.PUBLIC); proxyClassType.addMethod(ctor); ctor = new MethodInfo(proxyClassType.getConstPool(), "", "(Ljava/lang/Byte;Ljava/lang/Byte;)V"); b = new Bytecode(proxyClassType.getConstPool(), 3, 3); b.add(Opcode.ALOAD_0); b.add(Opcode.ACONST_NULL); b.addInvokespecial(proxyClassType.getName(), "", "(Ljava/lang/Byte;)V"); b.add(Opcode.RETURN); ctor.setCodeAttribute(b.toCodeAttribute()); ctor.setAccessFlags(AccessFlag.PUBLIC); proxyClassType.addMethod(ctor); } catch (DuplicateMemberException e) { throw new RuntimeException(e); } } public Class getBeanType() { return beanType; } public Set> getAdditionalInterfaces() { return additionalInterfaces; } public Bean getBean() { return bean; } /** * Figures out the correct class loader to use for a proxy for a given bean */ public static ClassLoader resolveClassLoaderForBeanProxy(Bean bean, TypeInfo typeInfo) { Class superClass = typeInfo.getSuperClass(); if (superClass.getName().startsWith("java")) { ClassLoader cl = Container.instance().services().get(ProxyServices.class).getClassLoader(bean.getBeanClass()); if (cl == null) { cl = Thread.currentThread().getContextClassLoader(); } return cl; } return Container.instance().services().get(ProxyServices.class).getClassLoader(superClass); } public static ClassLoader resolveClassLoaderForBeanProxy(Bean bean) { return resolveClassLoaderForBeanProxy(bean, TypeInfo.of(bean.getTypes())); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy