Home | History | Annotate | Download | only in proxy
      1 /*
      2  * Copyright 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 org.mockito.cglib.proxy;
     17 
     18 import java.lang.reflect.InvocationTargetException;
     19 import java.lang.reflect.Method;
     20 
     21 import org.mockito.cglib.core.AbstractClassGenerator;
     22 import org.mockito.cglib.core.CodeGenerationException;
     23 import org.mockito.cglib.core.GeneratorStrategy;
     24 import org.mockito.cglib.core.NamingPolicy;
     25 import org.mockito.cglib.core.Signature;
     26 import org.mockito.cglib.reflect.FastClass;
     27 
     28 
     29 /**
     30  * Classes generated by {@link Enhancer} pass this object to the
     31  * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can
     32  * be used to either invoke the original method, or call the same method on a different
     33  * object of the same type.
     34  * @version $Id: MethodProxy.java,v 1.14 2008/05/26 04:05:50 herbyderby Exp $
     35  */
     36 public class MethodProxy {
     37     private Signature sig1;
     38     private Signature sig2;
     39     private CreateInfo createInfo;
     40 
     41     private final Object initLock = new Object();
     42     private volatile FastClassInfo fastClassInfo;
     43 
     44     /**
     45      * For internal use by {@link Enhancer} only; see the {@link org.mockito.cglib.reflect.FastMethod} class
     46      * for similar functionality.
     47      */
     48     public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
     49         MethodProxy proxy = new MethodProxy();
     50         proxy.sig1 = new Signature(name1, desc);
     51         proxy.sig2 = new Signature(name2, desc);
     52         proxy.createInfo = new CreateInfo(c1, c2);
     53         return proxy;
     54     }
     55 
     56     private void init()
     57     {
     58         /*
     59          * Using a volatile invariant allows us to initialize the FastClass and
     60          * method index pairs atomically.
     61          *
     62          * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this
     63          * code could allow fastClassInfo to be instantiated more than once, which
     64          * appears to be benign.
     65          */
     66         if (fastClassInfo == null)
     67         {
     68             synchronized (initLock)
     69             {
     70                 if (fastClassInfo == null)
     71                 {
     72                     CreateInfo ci = createInfo;
     73 
     74                     FastClassInfo fci = new FastClassInfo();
     75                     fci.f1 = helper(ci, ci.c1);
     76                     fci.f2 = helper(ci, ci.c2);
     77                     fci.i1 = fci.f1.getIndex(sig1);
     78                     fci.i2 = fci.f2.getIndex(sig2);
     79                     fastClassInfo = fci;
     80                 }
     81             }
     82         }
     83     }
     84 
     85     private static class FastClassInfo
     86     {
     87         FastClass f1;
     88         FastClass f2;
     89         int i1;
     90         int i2;
     91     }
     92 
     93     private static class CreateInfo
     94     {
     95         Class c1;
     96         Class c2;
     97         NamingPolicy namingPolicy;
     98         GeneratorStrategy strategy;
     99         boolean attemptLoad;
    100 
    101         public CreateInfo(Class c1, Class c2)
    102         {
    103             this.c1 = c1;
    104             this.c2 = c2;
    105             AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
    106             if (fromEnhancer != null) {
    107                 namingPolicy = fromEnhancer.getNamingPolicy();
    108                 strategy = fromEnhancer.getStrategy();
    109                 attemptLoad = fromEnhancer.getAttemptLoad();
    110             }
    111         }
    112     }
    113 
    114     private static FastClass helper(CreateInfo ci, Class type) {
    115         FastClass.Generator g = new FastClass.Generator();
    116         g.setType(type);
    117         g.setClassLoader(ci.c2.getClassLoader());
    118         g.setNamingPolicy(ci.namingPolicy);
    119         g.setStrategy(ci.strategy);
    120         g.setAttemptLoad(ci.attemptLoad);
    121         return g.create();
    122     }
    123 
    124     private MethodProxy() {
    125     }
    126 
    127     /**
    128      * Return the signature of the proxied method.
    129      */
    130     public Signature getSignature() {
    131         return sig1;
    132     }
    133 
    134     /**
    135      * Return the name of the synthetic method created by CGLIB which is
    136      * used by {@link #invokeSuper} to invoke the superclass
    137      * (non-intercepted) method implementation. The parameter types are
    138      * the same as the proxied method.
    139      */
    140     public String getSuperName() {
    141         return sig2.getName();
    142     }
    143 
    144     /**
    145      * Return the {@link org.mockito.cglib.reflect.FastClass} method index
    146      * for the method used by {@link #invokeSuper}. This index uniquely
    147      * identifies the method within the generated proxy, and therefore
    148      * can be useful to reference external metadata.
    149      * @see #getSuperName
    150      */
    151     public int getSuperIndex() {
    152         init();
    153         return fastClassInfo.i2;
    154     }
    155 
    156     /**
    157      * Return the <code>MethodProxy</code> used when intercepting the method
    158      * matching the given signature.
    159      * @param type the class generated by Enhancer
    160      * @param sig the signature to match
    161      * @return the MethodProxy instance, or null if no applicable matching method is found
    162      * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
    163      */
    164     public static MethodProxy find(Class type, Signature sig) {
    165         try {
    166             Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
    167                                               MethodInterceptorGenerator.FIND_PROXY_TYPES);
    168             return (MethodProxy)m.invoke(null, new Object[]{ sig });
    169         } catch (NoSuchMethodException e) {
    170             throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
    171         } catch (IllegalAccessException e) {
    172             throw new CodeGenerationException(e);
    173         } catch (InvocationTargetException e) {
    174             throw new CodeGenerationException(e);
    175         }
    176     }
    177 
    178     /**
    179      * Invoke the original method, on a different object of the same type.
    180      * @param obj the compatible object; recursion will result if you use the object passed as the first
    181      * argument to the MethodInterceptor (usually not what you want)
    182      * @param args the arguments passed to the intercepted method; you may substitute a different
    183      * argument array as long as the types are compatible
    184      * @see MethodInterceptor#intercept
    185      * @throws Throwable the bare exceptions thrown by the called method are passed through
    186      * without wrapping in an <code>InvocationTargetException</code>
    187      */
    188     public Object invoke(Object obj, Object[] args) throws Throwable {
    189         try {
    190             init();
    191             FastClassInfo fci = fastClassInfo;
    192             return fci.f1.invoke(fci.i1, obj, args);
    193         } catch (InvocationTargetException e) {
    194             throw e.getTargetException();
    195         } catch (IllegalArgumentException e) {
    196             if (fastClassInfo.i1 < 0)
    197                 throw new IllegalArgumentException("Protected method: " + sig1);
    198             throw e;
    199         }
    200     }
    201 
    202     /**
    203      * Invoke the original (super) method on the specified object.
    204      * @param obj the enhanced object, must be the object passed as the first
    205      * argument to the MethodInterceptor
    206      * @param args the arguments passed to the intercepted method; you may substitute a different
    207      * argument array as long as the types are compatible
    208      * @see MethodInterceptor#intercept
    209      * @throws Throwable the bare exceptions thrown by the called method are passed through
    210      * without wrapping in an <code>InvocationTargetException</code>
    211      */
    212     public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    213         try {
    214             init();
    215             FastClassInfo fci = fastClassInfo;
    216             return fci.f2.invoke(fci.i2, obj, args);
    217         } catch (InvocationTargetException e) {
    218             throw e.getTargetException();
    219         }
    220     }
    221 }
    222