Package org.jboss.aop.proxy.container

Source Code of org.jboss.aop.proxy.container.ContainerProxyFactory

/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt 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.aop.proxy.container;

import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.SerialVersionUID;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ParameterAnnotationsAttribute;
import javassist.bytecode.SignatureAttribute;
import javassist.bytecode.annotation.Annotation;

import org.jboss.aop.Advised;
import org.jboss.aop.Advisor;
import org.jboss.aop.AspectManager;
import org.jboss.aop.ClassContainer;
import org.jboss.aop.InstanceAdvised;
import org.jboss.aop.MethodInfo;
import org.jboss.aop.instrument.TransformerCommon;
import org.jboss.aop.introduction.InterfaceIntroduction;
import org.jboss.aop.util.JavassistMethodHashing;


/**
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @version $Revision: 67376 $
*/
public class ContainerProxyFactory
{
   private static final String ADVISED = Advised.class.getName();
   private static final String INSTANCE_ADVISED = InstanceAdvised.class.getName();
   private static final CtClass[] EMPTY_CTCLASS_ARRAY = new CtClass[0];
   public static final String PROXY_NAME_PREFIX = "AOPContainerProxy$";
  
   private static Object maplock = new Object();
   private static WeakHashMap proxyCache = new WeakHashMap();
   private static volatile int counter = 0;
  
   private static CtMethod setDelegateMethod;


   /** True if java.lang.Object should be used as the super class for this proxy */
   private boolean objectAsSuper;

   /** The Advisor for this proxy */
   private Advisor advisor;

   /** The class we are generating this proxy for */
   private Class clazz;

   /** The generated proxy */
   private CtClass proxy;
  
   /** The class pool for the proxy (i.e for the class we are proxying */
   private ClassPool pool;
  
   /** The interface introductions and mixins that should be used for this proxy*/
   private ArrayList mixins;
  
   /** True if we are proxying a class already woven by jboss aop, false otherwise */
   private boolean isAdvised;
  
   private CtConstructor defaultCtor;
  
   public static Class getProxyClass(Class clazz, AspectManager manager) throws Exception
   {
      ContainerProxyCacheKey key = new ContainerProxyCacheKey(clazz);
      ClassContainer container = getTempClassContainer(clazz, manager);
      return getProxyClass(false, key, container);
   }
  
   public static Class getProxyClass(boolean objectAsSuper, ContainerProxyCacheKey key, Advisor advisor)
           throws Exception
   {
      Class clazz = key.getClazz();
      // Don't make a proxy of a proxy !
      if (Delegate.class.isAssignableFrom(clazz)) clazz = clazz.getSuperclass();

      Class proxyClass = null;
      synchronized (maplock)
      {
         Map map = (Map)proxyCache.get(clazz);
         if (map == null)
         {
            map = new HashMap();
            proxyCache.put(clazz, map);
         }
         else
         {
            proxyClass = (Class) map.get(key);
         }
        
         if (proxyClass == null)
         {
            proxyClass = generateProxy(objectAsSuper, clazz, advisor);
            map.put(key, proxyClass);
         }
      }
      return proxyClass;
   }

   private static Class generateProxy(boolean objectAsSuper, Class clazz, Advisor advisor) throws Exception
   {
      ArrayList introductions = advisor.getInterfaceIntroductions();
      CtClass proxy = createProxyCtClass(objectAsSuper, introductions, clazz, advisor);
      ProtectionDomain pd = clazz.getProtectionDomain();
      Class proxyClass = TransformerCommon.toClass(proxy, pd);
      return proxyClass;
   }

   private static ClassProxyContainer getTempClassContainer(Class clazz, AspectManager manager)
   {
      ClassProxyContainer container = new ClassProxyContainer("temp", manager);
      container.setClass(clazz);

      Iterator it = container.getManager().getInterfaceIntroductions().values().iterator();
      while (it.hasNext())
      {
         InterfaceIntroduction intro = (InterfaceIntroduction) it.next();
         if (intro.matches(container, container.getClazz()))
         {
            container.addInterfaceIntroduction(intro);
         }
      }

      return container;
   }
  
   public static CtClass createProxyCtClass(boolean objectAsSuper, ArrayList mixins, Class clazz, Advisor advisor)
           throws Exception
   {
      ContainerProxyFactory factory = new ContainerProxyFactory(objectAsSuper, mixins, clazz, advisor);
      return factory.createProxyCtClass();
   }

  
   private ContainerProxyFactory(boolean objectAsSuper, ArrayList mixins, Class clazz, Advisor advisor)
   {
      this.objectAsSuper = objectAsSuper;
      this.mixins = mixins;
      this.clazz = clazz;
      this.advisor = advisor;
      isAdvised = Advised.class.isAssignableFrom(clazz);
   }
 
  
   private CtClass createProxyCtClass() throws Exception
   {
      this.pool = AspectManager.instance().findClassPool(clazz.getClassLoader());
      if (pool == null) throw new NullPointerException("Could not find ClassPool");

      createBasics();
      addMethodsAndMixins();
      overrideSpecialMethods(clazz, proxy);
     
      SerialVersionUID.setSerialVersionUID(proxy);
     
      return proxy;
   }
  

   private CtClass createBasics() throws Exception
   {
      Class proxySuper  = (objectAsSuper) ? Object.class : this.clazz;
      String classname = getClassName();

      CtClass template = pool.get("org.jboss.aop.proxy.container.ProxyTemplate");
      CtClass superclass = pool.get(proxySuper.getName());

      this.proxy = TransformerCommon.makeClass(pool, classname, superclass);
      proxy.addInterface(pool.get("org.jboss.aop.instrument.Untransformable"));
     
      //Add all the interfaces of the class
      Class[] interfaces = proxySuper.getInterfaces();
      for (int i = 0 ; i < interfaces.length ; i++)
      {
         CtClass interfaze = pool.get(interfaces[i].getName());
         proxy.addInterface(interfaze);
      }
     
      copyConstructors(superclass, proxy);
      addFieldFromTemplate(template, "mixins");

      //Add methods/fields needed for Delegate interface
      proxy.addInterface(pool.get("org.jboss.aop.proxy.container.Delegate"));
     
      addFieldFromTemplate(template, "delegate", superclass);
      addMethodFromTemplate(template, "getDelegate", "{ return delegate; }");
      setDelegateMethod = addMethodFromTemplate(template, "setDelegate", "{ this.delegate = (" + proxySuper.getName() + ")$1; }");

      //Add methods/fields needed for AspectManaged interface
      proxy.addInterface(pool.get("org.jboss.aop.proxy.container.AspectManaged"));

      addFieldFromTemplate(template, "currentAdvisor");
      addFieldFromTemplate(template, "classAdvisor");
      addMethodFromTemplate(template, "getAdvisor", "{return classAdvisor;}");
      addMethodFromTemplate(template, "setAdvisor", "{this.classAdvisor = $1;currentAdvisor = classAdvisor;}");
     
      addFieldFromTemplate(template, "metadata");
      addMethodFromTemplate(template, "setMetadata", "{this.metadata = $1;}");
     
      addFieldFromTemplate(template, "instanceAdvisor");    
      addMethodFromTemplate(template, "setInstanceAdvisor", instanceAdvisorSetterBody());
      addMethodFromTemplate(template, "getInstanceAdvisor", instanceAdvisorGetterBody());
     
      addMethodFromTemplate(template, "writeObject", writeObjectBody());
      addMethodFromTemplate(template, "readObject", readObjectBody(superclass));
     
      if (objectAsSuper)
      {
         addMethodFromTemplate(template, "equals", equalsBody());
         addMethodFromTemplate(template, "hashCode", hashCodeBody());
      }

      addMethodFromTemplate(template, "toString", toStringBody());

      copyAnnotations(superclass, proxy);
      copySignature(superclass, proxy);
     
      return proxy;
   }
  
   private String instanceAdvisorSetterBody()
   {
      return
         "{" +
         "   synchronized (this)" +
         "   {" +
         "   if (this.instanceAdvisor != null)" +
         "   {" +
         "      throw new RuntimeException(\"InstanceAdvisor already set\");" +
         "   }" +
         "   if (!($1 instanceof org.jboss.aop.proxy.container.InstanceProxyContainer))" +
         "   {" +
         "      throw new RuntimeException(\"Wrong type for instance advisor: \" + $1);" +
         "   }" +
         "   this.instanceAdvisor = $1;" +
         "   currentAdvisor = (org.jboss.aop.proxy.container.InstanceProxyContainer)$1;" +
         "   }" +
         "}";
   }

   private String instanceAdvisorGetterBody()
   {
      return
         "{" +
         "   synchronized(this)" +
         "   {" +
         "      if (instanceAdvisor == null)" +
         "      {" +
         "         org.jboss.aop.proxy.container.InstanceProxyContainer ipc = ((org.jboss.aop.proxy.container.ClassProxyContainer)currentAdvisor).createInstanceProxyContainer();" +
         "         setInstanceAdvisor(ipc);" +
         "      }" +
         "   }" +
         "   return instanceAdvisor;" +
         "}";
   }

   private String writeObjectBody()
   {
      return
         "{" +
         "   $1.writeObject(delegate);" +
         "   $1.writeObject(mixins);" +
         "   $1.writeObject(metadata);"+
         "   $1.writeObject(classAdvisor.getClazz());" +
         "}";
//       TODO add support for the instance advisors
   }
  
   private String readObjectBody(CtClass superclass)
   {
      return
         "{" +
         "   delegate = (" + superclass.getName() + ")$1.readObject();" +
         "   mixins = (Object[])$1.readObject();" +
         "   metadata = (org.jboss.aop.metadata.SimpleMetaData)$1.readObject();" +
         "   java.lang.Class clazz = (java.lang.Class)$1.readObject();" +
         "   org.jboss.aop.AspectManager manager = org.jboss.aop.AspectManager.getTopLevelAspectManager();" +
         "   classAdvisor = manager.findAdvisor(clazz);" +
         "   currentAdvisor = classAdvisor;" +
         "}";
       //TODO add support for instance advisors
   }
  
   private String equalsBody()
   {
      return
         "{" +
         "   if (delegate != null)" +
         "   {" +
         "      if ($1 != null && $1 instanceof org.jboss.aop.proxy.container.Delegate)" +
         "         $1 = ((org.jboss.aop.proxy.container.Delegate) $1).getDelegate();" +
         "      return delegate.equals($1);" +
         "   }" +
         "   else" +
         "      return super.equals($1);" +
         "}";
   }
  
   private String hashCodeBody()
   {
      return
         "{" +
         "   if (delegate != null)" +
         "      return delegate.hashCode();" +
         "   else" +
         "      return super.hashCode();" +
         "}";
   }
  
   private String toStringBody()
   {
      return
         "{" +
         "   if (delegate != null)" +
         "      return delegate.toString() + \" (proxied by \" + this.getClass().getName() + \"@\" + java.lang.Integer.toHexString(java.lang.System.identityHashCode(this)) + \")\";" +
         "   else" +
         "      return super.toString() + \" (empty proxy of \" + this.getClass().getSuperclass().getName() + \")\";" +
         "}";
   }

   private CtField addFieldFromTemplate(CtClass template, String name) throws Exception
   {
      return addFieldFromTemplate(template, name, null);
   }

   private CtField addFieldFromTemplate(CtClass template, String name, CtClass type) throws Exception
   {
      CtField templateField = template.getField(name);
      CtClass fieldType = (type == null) ? templateField.getType() : type;
      CtField field = new CtField(fieldType, name, proxy);
      field.setModifiers(templateField.getModifiers());
      proxy.addField(field);
      return field;
   }
  
   private CtMethod addMethodFromTemplate(CtClass template, String name, String body) throws Exception
   {
      CtMethod templateMethod = template.getDeclaredMethod(name);
      CtMethod method = CtNewMethod.make(templateMethod.getReturnType(), name, templateMethod.getParameterTypes(), templateMethod.getExceptionTypes(), body, proxy);
      method.setModifiers(templateMethod.getModifiers());
      proxy.addMethod(method);
      return method;
   }
  
   private void copyConstructors(CtClass superclass, CtClass proxy) throws Exception
   {
      CtConstructor[] ctors = superclass.getConstructors();
      int minParameters = Integer.MAX_VALUE;
      CtConstructor bestCtor = null;
      for (int i = 0 ; i < ctors.length ; i++)
      {
         CtClass[] params = ctors[i].getParameterTypes();
        
         CtConstructor ctor = CtNewConstructor.make(
            ctors[i].getParameterTypes(),
            ctors[i].getExceptionTypes(),
            CtNewConstructor.PASS_PARAMS,
            null,
            null,
            proxy);
         ctor.setModifiers(ctors[i].getModifiers());
         proxy.addConstructor(ctor);

         if (params.length < minParameters)
         {
            bestCtor = ctor;
            minParameters = params.length;
         }
         if (params.length == 0)
         {
            defaultCtor = ctor;
         }
      }
     
      if (minParameters > 0)
      {
         //We don't have a default constructor, we need to create one passing in null to the super ctor
         createDefaultConstructor(bestCtor);
      }
   }
  
   private void createDefaultConstructor(CtConstructor bestCtor) throws NotFoundException, CannotCompileException
   {
      CtClass params[] = bestCtor.getParameterTypes();
     
      StringBuffer superCall = new StringBuffer("super(");
     
      for (int i = 0 ; i < params.length ; i++)
      {
         if (i > 0)
         {
            superCall.append(", ");
         }
        
         superCall.append(getNullType(params[i]));
      }
     
      superCall.append(");");
     
      defaultCtor = CtNewConstructor.make(EMPTY_CTCLASS_ARRAY, EMPTY_CTCLASS_ARRAY, "{" + superCall.toString() + "}", proxy);
      proxy.addConstructor(defaultCtor);
   }

   private String getNullType(CtClass clazz)
   {
      if (!clazz.isPrimitive())
      {
         return "null";
      }
      else
      {
         if (clazz.equals(CtClass.booleanType)) return "false";
         else if (clazz.equals(CtClass.charType)) return "'0'";
         else if (clazz.equals(CtClass.byteType)) return "0";
         else if (clazz.equals(CtClass.shortType)) return "0";
         else if (clazz.equals(CtClass.intType)) return "0";
         else if (clazz.equals(CtClass.longType)) return "0L";
         else if (clazz.equals(CtClass.floatType)) return "0f";
         else if (clazz.equals(CtClass.doubleType)) return "0d";
         else return "";//void
      }
     
   }
  
   private void addMethodsAndMixins()throws Exception
   {
      HashSet addedMethods = new HashSet();
      createMixinsAndIntroductions(addedMethods);
      createProxyMethods(addedMethods);
   }

   private void createMixinsAndIntroductions(HashSet addedMethods) throws Exception
   {
      HashSet addedInterfaces = new HashSet();
      Set implementedInterfaces = interfacesAsSet();
     
      if (mixins != null)
      {
         HashMap intfs = new HashMap();
         HashMap mixinIntfs = new HashMap();
         ArrayList mixes = new ArrayList();
         for (int i = 0; i < mixins.size(); i++)
         {
            InterfaceIntroduction introduction = (InterfaceIntroduction) mixins.get(i);
            getIntroductionInterfaces(introduction, intfs, mixinIntfs, mixes, i);
         }
         if (mixes.size() > 0)
         {
            defaultCtor.insertAfter("mixins = new Object[" + mixes.size() + "];");
            for (int i = 0; i < mixes.size(); i++)
            {
               //If using a constructor and passing "this" as the parameters, the proxy gets used. The delegate (instance wrapped by proxy) is not
               //set in the proxy until later, and if the mixin implements Delegate it will get set with the "real" instance at this point.
               InterfaceIntroduction.Mixin mixin = (InterfaceIntroduction.Mixin) mixes.get(i);
               String initializer = (mixin.getConstruction() == null) ? ("new " + mixin.getClassName() + "()") : mixin.getConstruction();
               String code = "mixins[" + i + "] = " + initializer + ";";
               defaultCtor.insertAfter(code);
               setDelegateMethod.insertAfter("{if (org.jboss.aop.proxy.container.Delegate.class.isAssignableFrom(mixins[" + i + "].getClass())) " +
                     "((org.jboss.aop.proxy.container.Delegate)mixins[" + i + "]).setDelegate($1);}");
            }
         }
        
         //Now that we have added the mixins, add all the proxies methods to the added methods set
         HashMap allMethods = JavassistMethodHashing.getDeclaredMethodMap(proxy);
         addedMethods.addAll(allMethods.keySet());

         createMixins(addedMethods, mixes, addedInterfaces, implementedInterfaces);
         createIntroductions(addedMethods, intfs, addedInterfaces, implementedInterfaces);
      }
   }
  
   /**
    * Split the interface introduction into something we can work with
    *
    * @param intro The InterfaceIntroduction
    * @param intfs receives the interfaces from plain interface introductions
    * @param mixins receives the interfaces from mixins
    * @param mixes receives the actual InterfaceIntroduction.Mixin objects
    * @@param the index interface introduction this data comes from
    */
   private void getIntroductionInterfaces(InterfaceIntroduction intro, HashMap intfs, HashMap mixins, ArrayList mixes, int idx)
   {
      Iterator it = intro.getMixins().iterator();
      while (it.hasNext())
      {
         InterfaceIntroduction.Mixin mixin = (InterfaceIntroduction.Mixin) it.next();
         mixes.add(mixin);
         for (int i = 0; i < mixin.getInterfaces().length; i++)
         {
            if (intfs.containsKey(mixin.getInterfaces()[i]))
            {
               intfs.remove(mixin.getInterfaces()[i]);
              
            }
            if (mixins.containsKey(mixin.getInterfaces()[i]))
            {
               throw new RuntimeException("cannot have an IntroductionInterface that introduces several mixins with the same interfaces " + mixin.getInterfaces()[i]);
            }
            mixins.put(mixin.getInterfaces()[i], new Integer(idx));
         }
      }
      if (intro.getInterfaces() != null)
      {
         for (int i = 0; i < intro.getInterfaces().length; i++)
         {
            if (intfs.containsKey(intro.getInterfaces()[i]) || mixins.containsKey(intro.getInterfaces()[i]))
            {
               //Do nothing
            }
            else
            {
               intfs.put(intro.getInterfaces()[i], new Integer(idx));
            }
         }
      }
   }

   private void createMixins(HashSet addedMethods, ArrayList mixes, HashSet addedInterfaces, Set implementedInterfaces) throws Exception
   {
      for (int mixinId = 0 ; mixinId < mixes.size() ; mixinId++)
      {
         InterfaceIntroduction.Mixin mixin = (InterfaceIntroduction.Mixin)mixes.get(mixinId);
        
         String[] intfs = mixin.getInterfaces();
        
         for (int ifId = 0 ; ifId < intfs.length ; ifId++)
         {
            String intf = intfs[ifId];
            if (addedInterfaces.contains(intf)) throw new Exception("2 mixins are implementing the same interfaces " + intf);
            if (implementedInterfaces.contains(intf))  throw new Exception("Attempting to mixin interface already used by class " + intf);

            CtClass intfClass = pool.get(intf);
            CtMethod[] methods = intfClass.getMethods();
            HashSet mixinMethods = new HashSet();
            for (int m = 0; m < methods.length; m++)
            {
               if (methods[m].getDeclaringClass().getName().equals("java.lang.Object")) continue;
               Long hash = new Long(JavassistMethodHashing.methodHash(methods[m]));
               if (mixinMethods.contains(hash)) continue;
               if (addedMethods.contains(hash)) throw new Exception("More than one mixin has same method");
               mixinMethods.add(hash);
               addedMethods.add(hash);
               String aopReturnStr = (methods[m].getReturnType().equals(CtClass.voidType)) ? "" : "return ($r)";
               String returnStr = (methods[m].getReturnType().equals(CtClass.voidType)) ? "" : "return ";
               String args = "null";
               if (methods[m].getParameterTypes().length > 0) args = "$args";
               String code = "{   " +
                             "   try{" +
                             "      " + intf + " mixin = (" + intf + ")mixins[" + mixinId + "];" +
                             "       org.jboss.aop.MethodInfo mi = currentAdvisor.getMethodInfo(" + hash.longValue() + "L); " +
                             "       org.jboss.aop.advice.Interceptor[] interceptors = mi.getInterceptors();" +
                             "       if (mi != null && interceptors != (Object[])null && interceptors.length > 0) { " +
                             "          org.jboss.aop.proxy.container.ContainerProxyMethodInvocation invocation = new org.jboss.aop.proxy.container.ContainerProxyMethodInvocation(mi, interceptors, this); " +
                             "          invocation.setArguments(" + args + "); " +
                             "          invocation.setTargetObject(mixin); " +
                             "          invocation.setMetaData(metadata);" +
                             "          " + aopReturnStr + " invocation.invokeNext(); " +
                             "       } else { " +
                             "       " + returnStr + " mixin." + methods[m].getName() + "($$);" +
                             "       } " +
                             "    }finally{" +
                             "    }" +
                             "}";
               CtMethod newMethod = CtNewMethod.make(methods[m].getReturnType(), methods[m].getName(), methods[m].getParameterTypes(), methods[m].getExceptionTypes(), code, proxy);
               newMethod.setModifiers(Modifier.PUBLIC);
               proxy.addMethod(newMethod);

               copySignature(methods[m], newMethod);
            }

            proxy.addInterface(intfClass);
            addedInterfaces.add(intfClass.getName());
         }
      }
   }

   private void createProxyMethods(HashSet addedMethods) throws Exception
   {
      HashMap allMethods = JavassistMethodHashing.getMethodMap(proxy.getSuperclass());

      Iterator it = allMethods.entrySet().iterator();
      while (it.hasNext())
      {
         Map.Entry entry = (Map.Entry) it.next();
         CtMethod m = (CtMethod) entry.getValue();
         if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers()) || Modifier.isFinal(m.getModifiers())) continue;

         Long hash = (Long) entry.getKey();
         if (addedMethods.contains(hash)) continue;
         addedMethods.add(hash);
         String aopReturnStr = (m.getReturnType().equals(CtClass.voidType)) ? "" : "return ($r)";
         String returnStr = (m.getReturnType().equals(CtClass.voidType)) ? "" : "return ";
         String args = "null";
        
         String name = null;
         if (isAdvised)
         {
            MethodInfo info = advisor.getMethodInfo(hash.longValue());
            Method originalMethod = info.getUnadvisedMethod();
            name = originalMethod.getName();
         }
         else
         {
            name = m.getName();
         }
        
         if (m.getParameterTypes().length > 0) args = "$args";
         String code = "{   " +
                       "    boolean handled = false;" +
                       "    try{" +
                       "       if (currentAdvisor != null) {" +
                       "          org.jboss.aop.MethodInfo mi = currentAdvisor.getMethodInfo(" + hash.longValue() + "L); " +
                       "          if (mi == null) " +
                       "             throw new java.lang.NoSuchMethodError(\"" + m.getName() + m.getSignature() + "\");" +
                       "          org.jboss.aop.advice.Interceptor[] interceptors = mi.getInterceptors(); " +
                       "          if (interceptors != (Object[])null && interceptors.length > 0) { " +
                       "             handled = true;" +
                       "             org.jboss.aop.proxy.container.ContainerProxyMethodInvocation invocation = new org.jboss.aop.proxy.container.ContainerProxyMethodInvocation(mi, interceptors, this); " +
                       "             invocation.setArguments(" + args + "); " +
                       "             invocation.setTargetObject(delegate); " +
                       "             invocation.setMetaData(metadata);" +
                       "             " + aopReturnStr + " invocation.invokeNext(); " +
                       "          }" +
                       "       }" +
                       "       if (!handled && delegate != null){ " +
                       "          " + returnStr + " delegate." + name + "($$); " +
                       "       }" +
                       "       return " + getNullType(m.getReturnType()) + ";" +
                       "    }finally{" +
                       "    }" +
                       "}";
         CtMethod newMethod = CtNewMethod.make(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes(), code, proxy);
         newMethod.setModifiers(Modifier.PUBLIC);
         proxy.addMethod(newMethod);

         copyAnnotations(m, newMethod);
         copySignature(m, newMethod);
      }
   }
  
   private void createIntroductions(HashSet addedMethods, HashMap intfs, HashSet addedInterfaces, Set implementedInterfaces) throws Exception
   {
      Iterator it = intfs.keySet().iterator();
      while (it.hasNext())
      {
         String intf = (String) it.next();
         if (addedInterfaces.contains(intf)) throw new Exception("2 mixins are implementing the same interfaces");
         if (implementedInterfaces.contains(intf)) 
         {
            continue;
         }

         CtClass intfClass = pool.get(intf);
         CtMethod[] methods = intfClass.getMethods();
         HashSet mixinMethods = new HashSet();
         for (int m = 0; m < methods.length; m++)
         {
            if (methods[m].getDeclaringClass().getName().equals("java.lang.Object")) continue;
           
            Long hash = new Long(JavassistMethodHashing.methodHash(methods[m]));
            if (mixinMethods.contains(hash)) continue;
            if (addedMethods.contains(hash)) continue;

            mixinMethods.add(hash);
            addedMethods.add(hash);
            String aopReturnStr = (methods[m].getReturnType().equals(CtClass.voidType)) ? "" : "return ($r)";
            String args = "null";
            if (methods[m].getParameterTypes().length > 0) args = "$args";
            String code = "{   " +
                          "    try{" +
                          "       org.jboss.aop.MethodInfo mi = currentAdvisor.getMethodInfo(" + hash.longValue() + "L); " +
                          "       if (mi == null) " +
                          "          throw new java.lang.NoSuchMethodError(\"" + methods[m].getName() + methods[m].getSignature() + "\");" +
                          "       org.jboss.aop.advice.Interceptor[] interceptors = mi.getInterceptors();" +
                          "       org.jboss.aop.proxy.container.ContainerProxyMethodInvocation invocation = new org.jboss.aop.proxy.container.ContainerProxyMethodInvocation(mi, interceptors, this); " +
                          "       invocation.setArguments(" + args + "); " +
                          "       invocation.setTargetObject(delegate); " +
                          "       invocation.setMetaData(metadata);" +
                          "       " + aopReturnStr + " invocation.invokeNext(); " +
                          "    }finally{" +
                          "    }" +
                          "}";
           
            CtMethod newMethod = CtNewMethod.make(methods[m].getReturnType(), methods[m].getName(), methods[m].getParameterTypes(), methods[m].getExceptionTypes(), code, proxy);
            newMethod.setModifiers(Modifier.PUBLIC);
            proxy.addMethod(newMethod);
           
            copySignature(methods[m], newMethod);
         }

         proxy.addInterface(intfClass);
         addedInterfaces.add(intfClass.getName());
      }
   }

   private Set interfacesAsSet() throws NotFoundException
   {
      HashSet set = new HashSet();
      CtClass[] interfaces = proxy.getInterfaces();
     
      for (int i = 0 ; i < interfaces.length ; i++)
      {
         set.add(interfaces[i].getName());
      }
     
      return set;
   }
  
   private String getClassName()
   {
      String packageName = clazz.getPackage().getName();
      if (!packageName.startsWith("java.") && !packageName.startsWith("sun."))
      {
         packageName += ".";
      }
      else
      {
         packageName = "";
      }
     
      return packageName + PROXY_NAME_PREFIX + counter++;
   }

   private void overrideSpecialMethods(Class clazz, CtClass proxy) throws Exception
   {
      addInstanceAdvisedMethods(clazz, proxy);
   }

   /**
    * If the class is Advised, the _getInstanceAdvisor() and _setInstanceAdvisor() methods will
    * not have been overridden. Make sure that these methods work with the instance proxy container.
    */
   private void addInstanceAdvisedMethods(Class clazz, CtClass proxy) throws Exception
   {
      CtClass advisedInterface = null;
      CtClass interfaces[] = proxy.getInterfaces();
     
      for (int i = 0 ; i < interfaces.length ; i++)
      {
         if (interfaces[i].getName().equals(ADVISED))
         {
            advisedInterface = interfaces[i];
            break;
         }
      }
     
      if (advisedInterface != null)
      {
         CtMethod[] methods = advisedInterface.getMethods();
         for (int i = 0 ; i < methods.length ; i++)
         {
            if (methods[i].getDeclaringClass().getName().equals(INSTANCE_ADVISED))
            {
               String name = methods[i].getName();
               String body = null;
               if (name.equals("_getInstanceAdvisor"))
               {
                  body = "{ return getInstanceAdvisor(); }";
               }
               else if (name.equals("_setInstanceAdvisor"))
               {
                  body = "{ setInstanceAdvisor($1); }";
               }
              
               if (body != null)
               {
                  CtMethod m = CtNewMethod.make(methods[i].getReturnType(), methods[i].getName(), methods[i].getParameterTypes(), methods[i].getExceptionTypes(), body, proxy);
                  m.setModifiers(Modifier.PUBLIC);
                  proxy.addMethod(m);
               }
            }
         }
      }
   }
  
   private void copyAnnotations(CtMethod src, CtMethod dest) throws NotFoundException
   {
      javassist.bytecode.MethodInfo srcInfo = src.getMethodInfo2();
      javassist.bytecode.MethodInfo destInfo = dest.getMethodInfo2();
      copyAnnotations(srcInfo, destInfo, AnnotationsAttribute.invisibleTag);
      copyAnnotations(srcInfo, destInfo, AnnotationsAttribute.visibleTag);

      int numParams = src.getParameterTypes().length;
      copyParameterAnnotations(numParams, srcInfo, destInfo, ParameterAnnotationsAttribute.visibleTag);
      copyParameterAnnotations(numParams, srcInfo, destInfo, ParameterAnnotationsAttribute.invisibleTag);

   }
  
   private void copyAnnotations(javassist.bytecode.MethodInfo src, javassist.bytecode.MethodInfo dest, String annotationTag)
   {
      AnnotationsAttribute attribute = (AnnotationsAttribute) src.getAttribute(annotationTag);
      if (attribute != null)
      {
         dest.addAttribute(attribute.copy(dest.getConstPool(), new HashMap()));
      }
   }
  
   private void copyParameterAnnotations(int numParams, javassist.bytecode.MethodInfo src, javassist.bytecode.MethodInfo dest, String paramsTag)
   {
      ParameterAnnotationsAttribute params = (ParameterAnnotationsAttribute)src.getAttribute(paramsTag);
      if (params != null)
      {
         dest.addAttribute(params.copy(dest.getConstPool(), new HashMap()));
         ParameterAnnotationsAttribute srcParams = new ParameterAnnotationsAttribute(src.getConstPool(), paramsTag);
         Annotation[][] emptyParamAnnotations = new Annotation[numParams][];
         for (int i = 0 ; i < numParams ; i++)
         {
            emptyParamAnnotations[i] = new Annotation[0];
         }
         srcParams.setAnnotations(emptyParamAnnotations);
         src.addAttribute(srcParams);
      }
   }

   private void copyAnnotations(CtClass src, CtClass dest) throws NotFoundException
   {
      ClassFile srcFile = src.getClassFile2();
      ClassFile destFile = dest.getClassFile2();
      copyAnnotations(srcFile, destFile, AnnotationsAttribute.invisibleTag);
      copyAnnotations(srcFile, destFile, AnnotationsAttribute.visibleTag);
   }
  
   private void copyAnnotations(ClassFile src, ClassFile dest, String annotationTag)
   {
      AnnotationsAttribute attribute = (AnnotationsAttribute) src.getAttribute(annotationTag);
      if (attribute != null)
      {
         dest.addAttribute(attribute.copy(dest.getConstPool(), new HashMap()));
      }
   }
  
  
   private void copySignature(CtMethod src, CtMethod dest)
   {
      javassist.bytecode.MethodInfo srcInfo = src.getMethodInfo2();
      javassist.bytecode.MethodInfo destInfo = dest.getMethodInfo2();
     
      SignatureAttribute sig = (SignatureAttribute)srcInfo.getAttribute(SignatureAttribute.tag);
      if (sig != null)
      {
         destInfo.addAttribute(sig.copy(destInfo.getConstPool(), new HashMap()));
      }
   }
  
   private void copySignature(CtClass src, CtClass dest)
   {
      ClassFile srcFile = src.getClassFile2();
      ClassFile destFile = dest.getClassFile2();
     
      SignatureAttribute sig = (SignatureAttribute)srcFile.getAttribute(SignatureAttribute.tag);
      if (sig != null)
      {
         destFile.addAttribute(sig.copy(destFile.getConstPool(), new HashMap()));
      }
   }
}  
TOP

Related Classes of org.jboss.aop.proxy.container.ContainerProxyFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.