Save This Page
Home » cglib-src-2.2 » net.sf.cglib.proxy » [javadoc | source]
    1   /*
    2    * Copyright 2002,2003,2004 The Apache Software Foundation
    3    *
    4    *  Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    *  Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   package net.sf.cglib.proxy;
   17   
   18   import java.lang.reflect.Constructor;
   19   import java.lang.reflect.InvocationTargetException;
   20   import java.lang.reflect.Method;
   21   import java.util;
   22   import net.sf.cglib.core;
   23   import org.objectweb.asm.Attribute;
   24   import org.objectweb.asm.ClassVisitor;
   25   import org.objectweb.asm.Type;
   26   import org.objectweb.asm.Label;
   27   
   28   /**
   29    * Generates dynamic subclasses to enable method interception. This
   30    * class started as a substitute for the standard Dynamic Proxy support
   31    * included with JDK 1.3, but one that allowed the proxies to extend a
   32    * concrete base class, in addition to implementing interfaces. The dynamically
   33    * generated subclasses override the non-final methods of the superclass and
   34    * have hooks which callback to user-defined interceptor
   35    * implementations.
   36    * <p>
   37    * The original and most general callback type is the {@link MethodInterceptor}, which
   38    * in AOP terms enables "around advice"--that is, you can invoke custom code both before
   39    * and after the invocation of the "super" method. In addition you can modify the
   40    * arguments before calling the super method, or not call it at all.
   41    * <p>
   42    * Although <code>MethodInterceptor</code> is generic enough to meet any
   43    * interception need, it is often overkill. For simplicity and performance, additional
   44    * specialized callback types, such as {@link LazyLoader} are also available.
   45    * Often a single callback will be used per enhanced class, but you can control
   46    * which callback is used on a per-method basis with a {@link CallbackFilter}.
   47    * <p>
   48    * The most common uses of this class are embodied in the static helper methods. For
   49    * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
   50    * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
   51    * <p>
   52    * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
   53    * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
   54    * to change the callbacks of an existing object, as well as a faster and easier way to create
   55    * new instances of the same type.
   56    * <p>
   57    * For an almost drop-in replacement for
   58    * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
   59    */
   60   public class Enhancer extends AbstractClassGenerator
   61   {
   62       private static final CallbackFilter ALL_ZERO = new CallbackFilter(){
   63           public int accept(Method method) {
   64               return 0;
   65           }
   66       };
   67   
   68       private static final Source SOURCE = new Source(Enhancer.class.getName());
   69       private static final EnhancerKey KEY_FACTORY =
   70         (EnhancerKey)KeyFactory.create(EnhancerKey.class);
   71   
   72       private static final String BOUND_FIELD = "CGLIB$BOUND";
   73       private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
   74       private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
   75       private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
   76       private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
   77       private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
   78   
   79       private static final Type FACTORY =
   80         TypeUtils.parseType("net.sf.cglib.proxy.Factory");
   81       private static final Type ILLEGAL_STATE_EXCEPTION =
   82         TypeUtils.parseType("IllegalStateException");
   83       private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
   84         TypeUtils.parseType("IllegalArgumentException");
   85       private static final Type THREAD_LOCAL =
   86         TypeUtils.parseType("ThreadLocal");
   87       private static final Type CALLBACK =
   88         TypeUtils.parseType("net.sf.cglib.proxy.Callback");
   89       private static final Type CALLBACK_ARRAY =
   90         Type.getType(Callback[].class);
   91       private static final Signature CSTRUCT_NULL =
   92         TypeUtils.parseConstructor("");
   93       private static final Signature SET_THREAD_CALLBACKS =
   94         new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
   95       private static final Signature SET_STATIC_CALLBACKS =
   96         new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
   97       private static final Signature NEW_INSTANCE =
   98         new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY });
   99       private static final Signature MULTIARG_NEW_INSTANCE =
  100         new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{
  101             Constants.TYPE_CLASS_ARRAY,
  102             Constants.TYPE_OBJECT_ARRAY,
  103             CALLBACK_ARRAY,
  104         });
  105       private static final Signature SINGLE_NEW_INSTANCE =
  106         new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK });
  107       private static final Signature SET_CALLBACK =
  108         new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK });
  109       private static final Signature GET_CALLBACK =
  110         new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE });
  111       private static final Signature SET_CALLBACKS =
  112         new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
  113       private static final Signature GET_CALLBACKS =
  114         new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]);
  115       private static final Signature THREAD_LOCAL_GET =
  116         TypeUtils.parseSignature("Object get()");
  117       private static final Signature THREAD_LOCAL_SET =
  118         TypeUtils.parseSignature("void set(Object)");
  119       private static final Signature BIND_CALLBACKS =
  120         TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
  121   
  122       /** Internal interface, only public due to ClassLoader issues. */
  123       public interface EnhancerKey {
  124           public Object newInstance(String type,
  125                                     String[] interfaces,
  126                                     CallbackFilter filter,
  127                                     Type[] callbackTypes,
  128                                     boolean useFactory,
  129                                     boolean interceptDuringConstruction,
  130                                     Long serialVersionUID);
  131       }
  132   
  133       private Class[] interfaces;
  134       private CallbackFilter filter;
  135       private Callback[] callbacks;
  136       private Type[] callbackTypes;
  137       private boolean classOnly;
  138       private Class superclass;
  139       private Class[] argumentTypes;
  140       private Object[] arguments;
  141       private boolean useFactory = true;
  142       private Long serialVersionUID;
  143       private boolean interceptDuringConstruction = true;
  144   
  145       /**
  146        * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
  147        * object should be used for each generated object, and should not
  148        * be shared across threads. To create additional instances of a
  149        * generated class, use the <code>Factory</code> interface.
  150        * @see Factory
  151        */
  152       public Enhancer() {
  153           super(SOURCE);
  154       }
  155   
  156       /**
  157        * Set the class which the generated class will extend. As a convenience,
  158        * if the supplied superclass is actually an interface, <code>setInterfaces</code>
  159        * will be called with the appropriate argument instead.
  160        * A non-interface argument must not be declared as final, and must have an
  161        * accessible constructor.
  162        * @param superclass class to extend or interface to implement
  163        * @see #setInterfaces(Class[])
  164        */
  165       public void setSuperclass(Class superclass) {
  166           if (superclass != null && superclass.isInterface()) {
  167               setInterfaces(new Class[]{ superclass });
  168           } else if (superclass != null && superclass.equals(Object.class)) {
  169               // affects choice of ClassLoader
  170               this.superclass = null;
  171           } else {
  172               this.superclass = superclass;
  173           }
  174       }
  175   
  176       /**
  177        * Set the interfaces to implement. The <code>Factory</code> interface will
  178        * always be implemented regardless of what is specified here.
  179        * @param interfaces array of interfaces to implement, or null
  180        * @see Factory
  181        */
  182       public void setInterfaces(Class[] interfaces) {
  183           this.interfaces = interfaces;
  184       }
  185   
  186       /**
  187        * Set the {@link CallbackFilter} used to map the generated class' methods
  188        * to a particular callback index.
  189        * New object instances will always use the same mapping, but may use different
  190        * actual callback objects.
  191        * @param filter the callback filter to use when generating a new class
  192        * @see #setCallbacks
  193        */
  194       public void setCallbackFilter(CallbackFilter filter) {
  195           this.filter = filter;
  196       }
  197   
  198   
  199       /**
  200        * Set the single {@link Callback} to use.
  201        * Ignored if you use {@link #createClass}.
  202        * @param callback the callback to use for all methods
  203        * @see #setCallbacks
  204        */
  205       public void setCallback(final Callback callback) {
  206           setCallbacks(new Callback[]{ callback });
  207       }
  208   
  209       /**
  210        * Set the array of callbacks to use.
  211        * Ignored if you use {@link #createClass}.
  212        * You must use a {@link CallbackFilter} to specify the index into this
  213        * array for each method in the proxied class.
  214        * @param callbacks the callback array
  215        * @see #setCallbackFilter
  216        * @see #setCallback
  217        */
  218       public void setCallbacks(Callback[] callbacks) {
  219           if (callbacks != null && callbacks.length == 0) {
  220               throw new IllegalArgumentException("Array cannot be empty");
  221           }
  222           this.callbacks = callbacks;
  223       }
  224   
  225       /**
  226        * Set whether the enhanced object instances should implement
  227        * the {@link Factory} interface.
  228        * This was added for tools that need for proxies to be more
  229        * indistinguishable from their targets. Also, in some cases it may
  230        * be necessary to disable the <code>Factory</code> interface to
  231        * prevent code from changing the underlying callbacks.
  232        * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
  233        */
  234       public void setUseFactory(boolean useFactory) {
  235           this.useFactory = useFactory;
  236       }
  237   
  238       /**
  239        * Set whether methods called from within the proxy's constructer
  240        * will be intercepted. The default value is true. Unintercepted methods
  241        * will call the method of the proxy's base class, if it exists.
  242        * @param interceptDuringConstruction whether to intercept methods called from the constructor
  243        */
  244       public void setInterceptDuringConstruction(boolean interceptDuringConstruction) {
  245           this.interceptDuringConstruction = interceptDuringConstruction;
  246       }
  247   
  248       /**
  249        * Set the single type of {@link Callback} to use.
  250        * This may be used instead of {@link #setCallback} when calling
  251        * {@link #createClass}, since it may not be possible to have
  252        * an array of actual callback instances.
  253        * @param callbackType the type of callback to use for all methods
  254        * @see #setCallbackTypes
  255        */     
  256       public void setCallbackType(Class callbackType) {
  257           setCallbackTypes(new Class[]{ callbackType });
  258       }
  259       
  260       /**
  261        * Set the array of callback types to use.
  262        * This may be used instead of {@link #setCallbacks} when calling
  263        * {@link #createClass}, since it may not be possible to have
  264        * an array of actual callback instances.
  265        * You must use a {@link CallbackFilter} to specify the index into this
  266        * array for each method in the proxied class.
  267        * @param callbackTypes the array of callback types
  268        */
  269       public void setCallbackTypes(Class[] callbackTypes) {
  270           if (callbackTypes != null && callbackTypes.length == 0) {
  271               throw new IllegalArgumentException("Array cannot be empty");
  272           }
  273           this.callbackTypes = CallbackInfo.determineTypes(callbackTypes);
  274       }
  275   
  276       /**
  277        * Generate a new class if necessary and uses the specified
  278        * callbacks (if any) to create a new object instance.
  279        * Uses the no-arg constructor of the superclass.
  280        * @return a new instance
  281        */
  282       public Object create() {
  283           classOnly = false;
  284           argumentTypes = null;
  285           return createHelper();
  286       }
  287   
  288       /**
  289        * Generate a new class if necessary and uses the specified
  290        * callbacks (if any) to create a new object instance.
  291        * Uses the constructor of the superclass matching the <code>argumentTypes</code>
  292        * parameter, with the given arguments.
  293        * @param argumentTypes constructor signature
  294        * @param arguments compatible wrapped arguments to pass to constructor
  295        * @return a new instance
  296        */
  297       public Object create(Class[] argumentTypes, Object[] arguments) {
  298           classOnly = false;
  299           if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
  300               throw new IllegalArgumentException("Arguments must be non-null and of equal length");
  301           }
  302           this.argumentTypes = argumentTypes;
  303           this.arguments = arguments;
  304           return createHelper();
  305       }
  306   
  307       /**
  308        * Generate a new class if necessary and return it without creating a new instance.
  309        * This ignores any callbacks that have been set.
  310        * To create a new instance you will have to use reflection, and methods
  311        * called during the constructor will not be intercepted. To avoid this problem,
  312        * use the multi-arg <code>create</code> method.
  313        * @see #create(Class[], Object[])
  314        */
  315       public Class createClass() {
  316           classOnly = true;
  317           return (Class)createHelper();
  318       }
  319   
  320       /**
  321        * Insert a static serialVersionUID field into the generated class.
  322        * @param sUID the field value, or null to avoid generating field.
  323        */
  324       public void setSerialVersionUID(Long sUID) {
  325           serialVersionUID = sUID;
  326       }
  327   
  328       private void validate() {
  329           if (classOnly ^ (callbacks == null)) {
  330               if (classOnly) {
  331                   throw new IllegalStateException("createClass does not accept callbacks");
  332               } else {
  333                   throw new IllegalStateException("Callbacks are required");
  334               }
  335           }
  336           if (classOnly && (callbackTypes == null)) {
  337               throw new IllegalStateException("Callback types are required");
  338           }
  339           if (callbacks != null && callbackTypes != null) {
  340               if (callbacks.length != callbackTypes.length) {
  341                   throw new IllegalStateException("Lengths of callback and callback types array must be the same");
  342               }
  343               Type[] check = CallbackInfo.determineTypes(callbacks);
  344               for (int i = 0; i < check.length; i++) {
  345                   if (!check[i].equals(callbackTypes[i])) {
  346                       throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]);
  347                   }
  348               }
  349           } else if (callbacks != null) {
  350               callbackTypes = CallbackInfo.determineTypes(callbacks);
  351           }
  352           if (filter == null) {
  353               if (callbackTypes.length > 1) {
  354                   throw new IllegalStateException("Multiple callback types possible but no filter specified");
  355               }
  356               filter = ALL_ZERO;
  357           }
  358           if (interfaces != null) {
  359               for (int i = 0; i < interfaces.length; i++) {
  360                   if (interfaces[i] == null) {
  361                       throw new IllegalStateException("Interfaces cannot be null");
  362                   }
  363                   if (!interfaces[i].isInterface()) {
  364                       throw new IllegalStateException(interfaces[i] + " is not an interface");
  365                   }
  366               }
  367           }
  368       }
  369   
  370       private Object createHelper() {
  371           validate();
  372           if (superclass != null) {
  373               setNamePrefix(superclass.getName());
  374           } else if (interfaces != null) {
  375               setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
  376           }
  377           return super.create(KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
  378                                                       ReflectUtils.getNames(interfaces),
  379                                                       filter,
  380                                                       callbackTypes,
  381                                                       useFactory,
  382                                                       interceptDuringConstruction,
  383                                                       serialVersionUID));
  384       }
  385   
  386       protected ClassLoader getDefaultClassLoader() {
  387           if (superclass != null) {
  388               return superclass.getClassLoader();
  389           } else if (interfaces != null) {
  390               return interfaces[0].getClassLoader();
  391           } else {
  392               return null;
  393           }
  394       }
  395   
  396       private Signature rename(Signature sig, int index) {
  397           return new Signature("CGLIB$" + sig.getName() + "$" + index,
  398                                sig.getDescriptor());
  399       }
  400       
  401       /**
  402        * Finds all of the methods that will be extended by an
  403        * Enhancer-generated class using the specified superclass and
  404        * interfaces. This can be useful in building a list of Callback
  405        * objects. The methods are added to the end of the given list.  Due
  406        * to the subclassing nature of the classes generated by Enhancer,
  407        * the methods are guaranteed to be non-static, non-final, and
  408        * non-private. Each method signature will only occur once, even if
  409        * it occurs in multiple classes.
  410        * @param superclass the class that will be extended, or null
  411        * @param interfaces the list of interfaces that will be implemented, or null
  412        * @param methods the list into which to copy the applicable methods
  413        */
  414       public static void getMethods(Class superclass, Class[] interfaces, List methods)
  415       {
  416           getMethods(superclass, interfaces, methods, null, null);
  417       }
  418   
  419       private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic)
  420       {
  421           ReflectUtils.addAllMethods(superclass, methods);
  422           List target = (interfaceMethods != null) ? interfaceMethods : methods;
  423           if (interfaces != null) {
  424               for (int i = 0; i < interfaces.length; i++) {
  425                   if (interfaces[i] != Factory.class) {
  426                       ReflectUtils.addAllMethods(interfaces[i], target);
  427                   }
  428               }
  429           }
  430           if (interfaceMethods != null) {
  431               if (forcePublic != null) {
  432                   forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));
  433               }
  434               methods.addAll(interfaceMethods);
  435           }
  436           CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));
  437           CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
  438           CollectionUtils.filter(methods, new DuplicatesPredicate());
  439           CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));
  440       }
  441   
  442       public void generateClass(ClassVisitor v) throws Exception {
  443           Class sc = (superclass == null) ? Object.class : superclass;
  444   
  445           if (TypeUtils.isFinal(sc.getModifiers()))
  446               throw new IllegalArgumentException("Cannot subclass final class " + sc);
  447           List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
  448           filterConstructors(sc, constructors);
  449   
  450           // Order is very important: must add superclass, then
  451           // its superclass chain, then each interface and
  452           // its superinterfaces.
  453           List actualMethods = new ArrayList();
  454           List interfaceMethods = new ArrayList();
  455           final Set forcePublic = new HashSet();
  456           getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
  457   
  458           List methods = CollectionUtils.transform(actualMethods, new Transformer() {
  459               public Object transform(Object value) {
  460                   Method method = (Method)value;
  461                   int modifiers = Constants.ACC_FINAL
  462                       | (method.getModifiers()
  463                          & ~Constants.ACC_ABSTRACT
  464                          & ~Constants.ACC_NATIVE
  465                          & ~Constants.ACC_SYNCHRONIZED);
  466                   if (forcePublic.contains(MethodWrapper.create(method))) {
  467                       modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
  468                   }
  469                   return ReflectUtils.getMethodInfo(method, modifiers);
  470               }
  471           });
  472   
  473           ClassEmitter e = new ClassEmitter(v);
  474           e.begin_class(Constants.V1_2,
  475                         Constants.ACC_PUBLIC,
  476                         getClassName(),
  477                         Type.getType(sc),
  478                         (useFactory ?
  479                          TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
  480                          TypeUtils.getTypes(interfaces)),
  481                         Constants.SOURCE_FILE);
  482           List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
  483   
  484           e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
  485           if (!interceptDuringConstruction) {
  486               e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);
  487           }
  488           e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
  489           e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
  490           if (serialVersionUID != null) {
  491               e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);
  492           }
  493   
  494           for (int i = 0; i < callbackTypes.length; i++) {
  495               e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);
  496           }
  497   
  498           emitMethods(e, methods, actualMethods);
  499           emitConstructors(e, constructorInfo);
  500           emitSetThreadCallbacks(e);
  501           emitSetStaticCallbacks(e);
  502           emitBindCallbacks(e);
  503   
  504           if (useFactory) {
  505               int[] keys = getCallbackKeys();
  506               emitNewInstanceCallbacks(e);
  507               emitNewInstanceCallback(e);
  508               emitNewInstanceMultiarg(e, constructorInfo);
  509               emitGetCallback(e, keys);
  510               emitSetCallback(e, keys);
  511               emitGetCallbacks(e);
  512               emitSetCallbacks(e);
  513           }
  514   
  515           e.end_class();
  516       }
  517   
  518       /**
  519        * Filter the list of constructors from the superclass. The
  520        * constructors which remain will be included in the generated
  521        * class. The default implementation is to filter out all private
  522        * constructors, but subclasses may extend Enhancer to override this
  523        * behavior.
  524        * @param sc the superclass
  525        * @param constructors the list of all declared constructors from the superclass
  526        * @throws IllegalArgumentException if there are no non-private constructors
  527        */
  528       protected void filterConstructors(Class sc, List constructors) {
  529           CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
  530           if (constructors.size() == 0)
  531               throw new IllegalArgumentException("No visible constructors in " + sc);
  532       }
  533   
  534       protected Object firstInstance(Class type) throws Exception {
  535           if (classOnly) {
  536               return type;
  537           } else {
  538               return createUsingReflection(type);
  539           }
  540       }
  541   
  542       protected Object nextInstance(Object instance) {
  543           Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
  544           if (classOnly) {
  545               return protoclass;
  546           } else if (instance instanceof Factory) {
  547               if (argumentTypes != null) {
  548                   return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
  549               } else {
  550                   return ((Factory)instance).newInstance(callbacks);
  551               }
  552           } else {
  553               return createUsingReflection(protoclass);
  554           }
  555       }
  556   
  557       /**
  558        * Call this method to register the {@link Callback} array to use before
  559        * creating a new instance of the generated class via reflection. If you are using
  560        * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
  561        * new instances, this method is unnecessary. Its primary use is for when you want to
  562        * cache and reuse a generated class yourself, and the generated class does
  563        * <i>not</i> implement the {@link Factory} interface.
  564        * <p>
  565        * Note that this method only registers the callbacks on the current thread.
  566        * If you want to register callbacks for instances created by multiple threads,
  567        * use {@link #registerStaticCallbacks}.
  568        * <p>
  569        * The registered callbacks are overwritten and subsequently cleared
  570        * when calling any of the <code>create</code> methods (such as
  571        * {@link #create}), or any {@link Factory} <code>newInstance</code> method.
  572        * Otherwise they are <i>not</i> cleared, and you should be careful to set them
  573        * back to <code>null</code> after creating new instances via reflection if
  574        * memory leakage is a concern.
  575        * @param generatedClass a class previously created by {@link Enhancer}
  576        * @param callbacks the array of callbacks to use when instances of the generated
  577        * class are created
  578        * @see #setUseFactory
  579        */
  580       public static void registerCallbacks(Class generatedClass, Callback[] callbacks) {
  581           setThreadCallbacks(generatedClass, callbacks);
  582       }
  583   
  584       /**
  585        * Similar to {@link #registerCallbacks}, but suitable for use
  586        * when multiple threads will be creating instances of the generated class.
  587        * The thread-level callbacks will always override the static callbacks.
  588        * Static callbacks are never cleared.
  589        * @param generatedClass a class previously created by {@link Enhancer}
  590        * @param callbacks the array of callbacks to use when instances of the generated
  591        * class are created
  592        */
  593       public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) {
  594           setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
  595       }
  596   
  597       /**
  598        * Determine if a class was generated using <code>Enhancer</code>.
  599        * @param type any class
  600        * @return whether the class was generated  using <code>Enhancer</code>
  601        */
  602       public static boolean isEnhanced(Class type) {
  603           try {
  604               getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
  605               return true;
  606           } catch (NoSuchMethodException e) {
  607               return false;
  608           }
  609       }
  610   
  611       private static void setThreadCallbacks(Class type, Callback[] callbacks) {
  612           setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
  613       }
  614   
  615       private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
  616           // TODO: optimize
  617           try {
  618               Method setter = getCallbacksSetter(type, methodName);
  619               setter.invoke(null, new Object[]{ callbacks });
  620           } catch (NoSuchMethodException e) {
  621               throw new IllegalArgumentException(type + " is not an enhanced class");
  622           } catch (IllegalAccessException e) {
  623               throw new CodeGenerationException(e);
  624           } catch (InvocationTargetException e) {
  625               throw new CodeGenerationException(e);
  626           }
  627       }
  628   
  629       private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
  630           return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
  631       }
  632   
  633       private Object createUsingReflection(Class type) {
  634           setThreadCallbacks(type, callbacks);
  635           try{
  636           
  637           if (argumentTypes != null) {
  638           	
  639                return ReflectUtils.newInstance(type, argumentTypes, arguments);
  640                
  641           } else {
  642           	
  643               return ReflectUtils.newInstance(type);
  644               
  645           }
  646           }finally{
  647            // clear thread callbacks to allow them to be gc'd
  648            setThreadCallbacks(type, null);
  649           }
  650       }
  651   
  652       /**
  653        * Helper method to create an intercepted object.
  654        * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
  655        * instead of this static method.
  656        * @param type class to extend or interface to implement
  657        * @param callback the callback to use for all methods
  658        */
  659       public static Object create(Class type, Callback callback) {
  660           Enhancer e = new Enhancer();
  661           e.setSuperclass(type);
  662           e.setCallback(callback);
  663           return e.create();
  664       }
  665   
  666       /**
  667        * Helper method to create an intercepted object.
  668        * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
  669        * instead of this static method.
  670        * @param type class to extend or interface to implement
  671        * @param interfaces array of interfaces to implement, or null
  672        * @param callback the callback to use for all methods
  673        */
  674       public static Object create(Class superclass, Class interfaces[], Callback callback) {
  675           Enhancer e = new Enhancer();
  676           e.setSuperclass(superclass);
  677           e.setInterfaces(interfaces);
  678           e.setCallback(callback);
  679           return e.create();
  680       }
  681   
  682       /**
  683        * Helper method to create an intercepted object.
  684        * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
  685        * instead of this static method.
  686        * @param type class to extend or interface to implement
  687        * @param interfaces array of interfaces to implement, or null
  688        * @param filter the callback filter to use when generating a new class
  689        * @param callbacks callback implementations to use for the enhanced object
  690        */
  691       public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) {
  692           Enhancer e = new Enhancer();
  693           e.setSuperclass(superclass);
  694           e.setInterfaces(interfaces);
  695           e.setCallbackFilter(filter);
  696           e.setCallbacks(callbacks);
  697           return e.create();
  698       }
  699   
  700       private void emitConstructors(ClassEmitter ce, List constructors) {
  701           boolean seenNull = false;
  702           for (Iterator it = constructors.iterator(); it.hasNext();) {
  703               MethodInfo constructor = (MethodInfo)it.next();
  704               CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC);
  705               e.load_this();
  706               e.dup();
  707               e.load_args();
  708               Signature sig = constructor.getSignature();
  709               seenNull = seenNull || sig.getDescriptor().equals("()V");
  710               e.super_invoke_constructor(sig);
  711               e.invoke_static_this(BIND_CALLBACKS);
  712               if (!interceptDuringConstruction) {
  713                   e.load_this();
  714                   e.push(1);
  715                   e.putfield(CONSTRUCTED_FIELD);
  716               }
  717               e.return_value();
  718               e.end_method();
  719           }
  720           if (!classOnly && !seenNull && arguments == null)
  721               throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given");
  722       }
  723   
  724       private int[] getCallbackKeys() {
  725           int[] keys = new int[callbackTypes.length];
  726           for (int i = 0; i < callbackTypes.length; i++) {
  727               keys[i] = i;
  728           }
  729           return keys;
  730       }
  731   
  732       private void emitGetCallback(ClassEmitter ce, int[] keys) {
  733           final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null);
  734           e.load_this();
  735           e.invoke_static_this(BIND_CALLBACKS);
  736           e.load_this();
  737           e.load_arg(0);
  738           e.process_switch(keys, new ProcessSwitchCallback() {
  739               public void processCase(int key, Label end) {
  740                   e.getfield(getCallbackField(key));
  741                   e.goTo(end);
  742               }
  743               public void processDefault() {
  744                   e.pop(); // stack height
  745                   e.aconst_null();
  746               }
  747           });
  748           e.return_value();
  749           e.end_method();
  750       }
  751   
  752       private void emitSetCallback(ClassEmitter ce, int[] keys) {
  753           final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null);
  754           e.load_arg(0);
  755           e.process_switch(keys, new ProcessSwitchCallback() {
  756               public void processCase(int key, Label end) {
  757                   e.load_this();
  758                   e.load_arg(1);
  759                   e.checkcast(callbackTypes[key]);
  760                   e.putfield(getCallbackField(key));
  761                   e.goTo(end);
  762               }
  763               public void processDefault() {
  764                   // TODO: error?
  765               }
  766           });
  767           e.return_value();
  768           e.end_method();
  769       }
  770   
  771       private void emitSetCallbacks(ClassEmitter ce) {
  772           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null);
  773           e.load_this();
  774           e.load_arg(0);
  775           for (int i = 0; i < callbackTypes.length; i++) {
  776               e.dup2();
  777               e.aaload(i);
  778               e.checkcast(callbackTypes[i]);
  779               e.putfield(getCallbackField(i));
  780           }
  781           e.return_value();
  782           e.end_method();
  783       }
  784   
  785       private void emitGetCallbacks(ClassEmitter ce) {
  786           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null);
  787           e.load_this();
  788           e.invoke_static_this(BIND_CALLBACKS);
  789           e.load_this();
  790           e.push(callbackTypes.length);
  791           e.newarray(CALLBACK);
  792           for (int i = 0; i < callbackTypes.length; i++) {
  793               e.dup();
  794               e.push(i);
  795               e.load_this();
  796               e.getfield(getCallbackField(i));
  797               e.aastore();
  798           }
  799           e.return_value();
  800           e.end_method();
  801       }
  802   
  803       private void emitNewInstanceCallbacks(ClassEmitter ce) {
  804           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
  805           e.load_arg(0);
  806           e.invoke_static_this(SET_THREAD_CALLBACKS);
  807           emitCommonNewInstance(e);
  808       }
  809   
  810       private void emitCommonNewInstance(CodeEmitter e) {
  811           e.new_instance_this();
  812           e.dup();
  813           e.invoke_constructor_this();
  814           e.aconst_null();
  815           e.invoke_static_this(SET_THREAD_CALLBACKS);
  816           e.return_value();
  817           e.end_method();
  818       }
  819       
  820       private void emitNewInstanceCallback(ClassEmitter ce) {
  821           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null);
  822           switch (callbackTypes.length) {
  823           case 0:
  824               // TODO: make sure Callback is null
  825               break;
  826           case 1:
  827               // for now just make a new array; TODO: optimize
  828               e.push(1);
  829               e.newarray(CALLBACK);
  830               e.dup();
  831               e.push(0);
  832               e.load_arg(0);
  833               e.aastore();
  834               e.invoke_static_this(SET_THREAD_CALLBACKS);
  835               break;
  836           default:
  837               e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
  838           }
  839           emitCommonNewInstance(e);
  840       }
  841   
  842       private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) {
  843           final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null);
  844           e.load_arg(2);
  845           e.invoke_static_this(SET_THREAD_CALLBACKS);
  846           e.new_instance_this();
  847           e.dup();
  848           e.load_arg(0);
  849           EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
  850               public void processCase(Object key, Label end) {
  851                   MethodInfo constructor = (MethodInfo)key;
  852                   Type types[] = constructor.getSignature().getArgumentTypes();
  853                   for (int i = 0; i < types.length; i++) {
  854                       e.load_arg(1);
  855                       e.push(i);
  856                       e.aaload();
  857                       e.unbox(types[i]);
  858                   }
  859                   e.invoke_constructor_this(constructor.getSignature());
  860                   e.goTo(end);
  861               }
  862               public void processDefault() {
  863                   e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
  864               }
  865           });
  866           e.aconst_null();
  867           e.invoke_static_this(SET_THREAD_CALLBACKS);
  868           e.return_value();
  869           e.end_method();
  870       }
  871   
  872       private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) {
  873           CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes);
  874   
  875           Map groups = new HashMap();
  876           final Map indexes = new HashMap();
  877           final Map originalModifiers = new HashMap();
  878           final Map positions = CollectionUtils.getIndexMap(methods);
  879   
  880           Iterator it1 = methods.iterator();
  881           Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null;
  882   
  883           while (it1.hasNext()) {
  884               MethodInfo method = (MethodInfo)it1.next();
  885               Method actualMethod = (it2 != null) ? (Method)it2.next() : null;
  886               int index = filter.accept(actualMethod);
  887               if (index >= callbackTypes.length) {
  888                   throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
  889               }
  890               originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()));
  891               indexes.put(method, new Integer(index));
  892               List group = (List)groups.get(generators[index]);
  893               if (group == null) {
  894                   groups.put(generators[index], group = new ArrayList(methods.size()));
  895               }
  896               group.add(method);
  897           }
  898   
  899           Set seenGen = new HashSet();
  900           CodeEmitter se = ce.getStaticHook();
  901           se.new_instance(THREAD_LOCAL);
  902           se.dup();
  903           se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
  904           se.putfield(THREAD_CALLBACKS_FIELD);
  905   
  906           final Object[] state = new Object[1];
  907           CallbackGenerator.Context context = new CallbackGenerator.Context() {
  908               public ClassLoader getClassLoader() {
  909                   return Enhancer.this.getClassLoader();
  910               }
  911               public int getOriginalModifiers(MethodInfo method) {
  912                   return ((Integer)originalModifiers.get(method)).intValue();
  913               }
  914               public int getIndex(MethodInfo method) {
  915                   return ((Integer)indexes.get(method)).intValue();
  916               }
  917               public void emitCallback(CodeEmitter e, int index) {
  918                   emitCurrentCallback(e, index);
  919               }
  920               public Signature getImplSignature(MethodInfo method) {
  921                   return rename(method.getSignature(), ((Integer)positions.get(method)).intValue());
  922               }
  923               public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) {
  924                   CodeEmitter e = EmitUtils.begin_method(ce, method);
  925                   if (!interceptDuringConstruction &&
  926                       !TypeUtils.isAbstract(method.getModifiers())) {
  927                       Label constructed = e.make_label();
  928                       e.load_this();
  929                       e.getfield(CONSTRUCTED_FIELD);
  930                       e.if_jump(e.NE, constructed);
  931                       e.load_this();
  932                       e.load_args();
  933                       e.super_invoke();
  934                       e.return_value();
  935                       e.mark(constructed);
  936                   }
  937                   return e;
  938               }
  939           };
  940           for (int i = 0; i < callbackTypes.length; i++) {
  941               CallbackGenerator gen = generators[i];
  942               if (!seenGen.contains(gen)) {
  943                   seenGen.add(gen);
  944                   final List fmethods = (List)groups.get(gen);
  945                   if (fmethods != null) {
  946                       try {
  947                           gen.generate(ce, context, fmethods);
  948                           gen.generateStatic(se, context, fmethods);
  949                       } catch (RuntimeException x) {
  950                           throw x;
  951                       } catch (Exception x) {
  952                           throw new CodeGenerationException(x);
  953                       }
  954                   }
  955               }
  956           }
  957           se.return_value();
  958           se.end_method();
  959       }
  960   
  961       private void emitSetThreadCallbacks(ClassEmitter ce) {
  962           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
  963                                           SET_THREAD_CALLBACKS,
  964                                           null);
  965           e.getfield(THREAD_CALLBACKS_FIELD);
  966           e.load_arg(0);
  967           e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
  968           e.return_value();
  969           e.end_method();
  970       }
  971   
  972       private void emitSetStaticCallbacks(ClassEmitter ce) {
  973           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
  974                                           SET_STATIC_CALLBACKS,
  975                                           null);
  976           e.load_arg(0);
  977           e.putfield(STATIC_CALLBACKS_FIELD);
  978           e.return_value();
  979           e.end_method();
  980       }
  981       
  982       private void emitCurrentCallback(CodeEmitter e, int index) {
  983           e.load_this();
  984           e.getfield(getCallbackField(index));
  985           e.dup();
  986           Label end = e.make_label();
  987           e.ifnonnull(end);
  988           e.pop(); // stack height
  989           e.load_this();
  990           e.invoke_static_this(BIND_CALLBACKS);
  991           e.load_this();
  992           e.getfield(getCallbackField(index));
  993           e.mark(end);
  994       }
  995   
  996       private void emitBindCallbacks(ClassEmitter ce) {
  997           CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
  998                                           BIND_CALLBACKS,
  999                                           null);
 1000           Local me = e.make_local();
 1001           e.load_arg(0);
 1002           e.checkcast_this();
 1003           e.store_local(me);
 1004   
 1005           Label end = e.make_label();
 1006           e.load_local(me);
 1007           e.getfield(BOUND_FIELD);
 1008           e.if_jump(e.NE, end);
 1009           e.load_local(me);
 1010           e.push(1);
 1011           e.putfield(BOUND_FIELD);
 1012   
 1013           e.getfield(THREAD_CALLBACKS_FIELD);
 1014           e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
 1015           e.dup();
 1016           Label found_callback = e.make_label();
 1017           e.ifnonnull(found_callback);
 1018           e.pop();
 1019   
 1020           e.getfield(STATIC_CALLBACKS_FIELD);
 1021           e.dup();
 1022           e.ifnonnull(found_callback);
 1023           e.pop();
 1024           e.goTo(end);
 1025   
 1026           e.mark(found_callback);
 1027           e.checkcast(CALLBACK_ARRAY);
 1028           e.load_local(me);
 1029           e.swap();
 1030           for (int i = callbackTypes.length - 1; i >= 0; i--) {
 1031               if (i != 0) {
 1032                   e.dup2();
 1033               }
 1034               e.aaload(i);
 1035               e.checkcast(callbackTypes[i]);
 1036               e.putfield(getCallbackField(i));
 1037           }
 1038   
 1039           e.mark(end);
 1040           e.return_value();
 1041           e.end_method();
 1042       }
 1043   
 1044       private static String getCallbackField(int index) {
 1045           return "CGLIB$CALLBACK_" + index;
 1046       }
 1047   }

Save This Page
Home » cglib-src-2.2 » net.sf.cglib.proxy » [javadoc | source]