/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat, Inc., 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.classfilewriter.util.DescriptorUtils.isPrimitive; import static org.jboss.classfilewriter.util.DescriptorUtils.isWide; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.security.AccessController; import java.util.HashSet; import java.util.Set; import org.jboss.classfilewriter.ClassFile; import org.jboss.classfilewriter.ClassMethod; import org.jboss.classfilewriter.DuplicateMemberException; import org.jboss.classfilewriter.code.CodeAttribute; import org.jboss.classfilewriter.util.Boxing; import org.jboss.classfilewriter.util.DescriptorUtils; import org.jboss.weld.annotated.enhanced.MethodSignature; import org.jboss.weld.annotated.enhanced.jlr.MethodSignatureImpl; import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack; import org.jboss.weld.exceptions.WeldException; import org.jboss.weld.injection.InterceptionFactoryImpl; import org.jboss.weld.logging.BeanLogger; import org.jboss.weld.security.GetDeclaredMethodsAction; import org.jboss.weld.util.bytecode.BytecodeUtils; import org.jboss.weld.util.bytecode.MethodInformation; import org.jboss.weld.util.bytecode.RuntimeMethodInformation; /** * Generates proxies used to apply intercptors to custom bean instances and return values of producer methods. * * @author Martin Kouba * * @param <T> * @see InterceptionFactoryImpl */ public class InterceptedProxyFactory<T> extends ProxyFactory<T> { public static final String PROXY_SUFFIX = "InterceptedProxy"; private static final String INVOKE = "invoke"; private static final String JAVA_LANG_OBJECT = "java.lang.Object"; private final Set<MethodSignature> enhancedMethodSignatures; private final Set<MethodSignature> interceptedMethodSignatures; private final String suffix; public InterceptedProxyFactory(String contextId, Class<?> proxiedBeanType, Set<? extends Type> typeClosure, Set<MethodSignature> enhancedMethodSignatures, Set<MethodSignature> interceptedMethodSignatures, String suffix) { super(contextId, proxiedBeanType, typeClosure, null); this.enhancedMethodSignatures = enhancedMethodSignatures; this.interceptedMethodSignatures = interceptedMethodSignatures; this.suffix = suffix; } protected String getProxyNameSuffix() { return PROXY_SUFFIX + suffix; } @Override protected void addMethodsFromClass(ClassFile proxyClassType, ClassMethod staticConstructor) { try { final Set<MethodSignature> finalMethods = new HashSet<MethodSignature>(); final Set<MethodSignature> processedBridgeMethods = new HashSet<MethodSignature>(); // Add all methods from the class hierarchy Class<?> cls = getBeanType(); while (cls != null) { Set<MethodSignature> declaredBridgeMethods = new HashSet<MethodSignature>(); for (Method method : AccessController.doPrivileged(new GetDeclaredMethodsAction(cls))) { final MethodSignatureImpl methodSignature = new MethodSignatureImpl(method); if (isMethodAccepted(method, getProxySuperclass()) && enhancedMethodSignatures.contains(methodSignature) && !finalMethods.contains(methodSignature) && !processedBridgeMethods.contains(methodSignature)) { try { final MethodInformation methodInfo = new RuntimeMethodInformation(method); ClassMethod classMethod = proxyClassType.addMethod(method); if (interceptedMethodSignatures.contains(methodSignature)) { // this method is intercepted final CodeAttribute b = classMethod.getCodeAttribute(); b.aload(0); getMethodHandlerField(classMethod.getClassFile(), b); // get the Stack b.invokestatic(InterceptionDecorationContext.class.getName(), "getStack", "()" + DescriptorUtils.makeDescriptor(Stack.class)); b.aload(0); DEFAULT_METHOD_RESOLVER.getDeclaredMethod(classMethod, methodInfo.getDeclaringClass(), method.getName(), methodInfo.getParameterTypes(), staticConstructor); b.dup(); // Params b.iconst(method.getParameterTypes().length); b.anewarray(JAVA_LANG_OBJECT); int localVariableCount = 1; for (int i = 0; i < method.getParameterTypes().length; ++i) { String typeString = methodInfo.getParameterTypes()[i]; b.dup(); // duplicate the array reference b.iconst(i); // load the parameter value BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount); // box the parameter if necessary Boxing.boxIfNessesary(b, typeString); // and store it in the array b.aastore(); if (isWide(typeString)) { localVariableCount = localVariableCount + 2; } else { localVariableCount++; } } b.invokeinterface(StackAwareMethodHandler.class.getName(), INVOKE, LJAVA_LANG_OBJECT, InterceptedSubclassFactory.INVOKE_METHOD_PARAMETERS); if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) { b.returnInstruction(); } else if (isPrimitive(methodInfo.getReturnType())) { Boxing.unbox(b, classMethod.getReturnType()); b.returnInstruction(); } else { b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType())); b.returnInstruction(); } BeanLogger.LOG.addingMethodToProxy(method); } else { final CodeAttribute b = classMethod.getCodeAttribute(); b.aload(0); getMethodHandlerField(classMethod.getClassFile(), b); b.aload(0); DEFAULT_METHOD_RESOLVER.getDeclaredMethod(classMethod, methodInfo.getDeclaringClass(), method.getName(), methodInfo.getParameterTypes(), staticConstructor); b.aconstNull(); b.iconst(method.getParameterTypes().length); b.anewarray(JAVA_LANG_OBJECT); int localVariableCount = 1; for (int i = 0; i < method.getParameterTypes().length; ++i) { String typeString = methodInfo.getParameterTypes()[i]; b.dup(); // duplicate the array reference b.iconst(i); // load the parameter value BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount); // box the parameter if necessary Boxing.boxIfNessesary(b, typeString); // and store it in the array b.aastore(); if (isWide(typeString)) { localVariableCount = localVariableCount + 2; } else { localVariableCount++; } } b.invokeinterface(MethodHandler.class.getName(), INVOKE, LJAVA_LANG_OBJECT, new String[] { LJAVA_LANG_OBJECT, LJAVA_LANG_REFLECT_METHOD, LJAVA_LANG_REFLECT_METHOD, "[" + LJAVA_LANG_OBJECT }); if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) { b.returnInstruction(); } else if (isPrimitive(methodInfo.getReturnType())) { Boxing.unbox(b, classMethod.getReturnType()); b.returnInstruction(); } else { b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType())); b.returnInstruction(); } } } catch (DuplicateMemberException e) { // do nothing. This will happen if superclass methods have // been overridden } } else { if (Modifier.isFinal(method.getModifiers())) { finalMethods.add(methodSignature); } if (method.isBridge()) { declaredBridgeMethods.add(methodSignature); } } } processedBridgeMethods.addAll(declaredBridgeMethods); cls = cls.getSuperclass(); } } catch (Exception e) { throw new WeldException(e); } } @Override protected boolean isMethodAccepted(Method method, Class<?> proxySuperclass) { return super.isMethodAccepted(method, proxySuperclass) && CommonProxiedMethodFilters.NON_PRIVATE.accept(method, proxySuperclass) && !method.isBridge(); } }