001/*
002 * Copyright 2003,2004 The Apache Software Foundation
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.cglib.proxy;
018
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021
022import org.springframework.cglib.core.AbstractClassGenerator;
023import org.springframework.cglib.core.CodeGenerationException;
024import org.springframework.cglib.core.GeneratorStrategy;
025import org.springframework.cglib.core.NamingPolicy;
026import org.springframework.cglib.core.Signature;
027import org.springframework.cglib.reflect.FastClass;
028
029/**
030 * Classes generated by {@link Enhancer} pass this object to the
031 * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can
032 * be used to either invoke the original method, or call the same method on a different
033 * object of the same type.
034 * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $
035 */
036@SuppressWarnings({"rawtypes", "unchecked"})
037public class MethodProxy {
038
039        private Signature sig1;
040
041        private Signature sig2;
042
043        private CreateInfo createInfo;
044
045        private final Object initLock = new Object();
046
047        private volatile FastClassInfo fastClassInfo;
048
049        /**
050         * For internal use by {@link Enhancer} only; see the {@link org.springframework.cglib.reflect.FastMethod} class
051         * for similar functionality.
052         */
053        public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
054                MethodProxy proxy = new MethodProxy();
055                proxy.sig1 = new Signature(name1, desc);
056                proxy.sig2 = new Signature(name2, desc);
057                proxy.createInfo = new CreateInfo(c1, c2);
058                return proxy;
059        }
060
061        private void init() {
062                /*
063                 * Using a volatile invariant allows us to initialize the FastClass and
064                 * method index pairs atomically.
065                 *
066                 * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this
067                 * code could allow fastClassInfo to be instantiated more than once, which
068                 * appears to be benign.
069                 */
070                if (fastClassInfo == null) {
071                        synchronized (initLock) {
072                                if (fastClassInfo == null) {
073                                        CreateInfo ci = createInfo;
074
075                                        FastClassInfo fci = new FastClassInfo();
076                                        fci.f1 = helper(ci, ci.c1);
077                                        fci.f2 = helper(ci, ci.c2);
078                                        fci.i1 = fci.f1.getIndex(sig1);
079                                        fci.i2 = fci.f2.getIndex(sig2);
080                                        fastClassInfo = fci;
081                                        createInfo = null;
082                                }
083                        }
084                }
085        }
086
087
088        private static class FastClassInfo {
089
090                FastClass f1;
091
092                FastClass f2;
093
094                int i1;
095
096                int i2;
097        }
098
099
100        private static class CreateInfo {
101
102                Class c1;
103
104                Class c2;
105
106                NamingPolicy namingPolicy;
107
108                GeneratorStrategy strategy;
109
110                boolean attemptLoad;
111
112                public CreateInfo(Class c1, Class c2) {
113                        this.c1 = c1;
114                        this.c2 = c2;
115                        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
116                        if (fromEnhancer != null) {
117                                namingPolicy = fromEnhancer.getNamingPolicy();
118                                strategy = fromEnhancer.getStrategy();
119                                attemptLoad = fromEnhancer.getAttemptLoad();
120                        }
121                }
122        }
123
124
125        private static FastClass helper(CreateInfo ci, Class type) {
126                FastClass.Generator g = new FastClass.Generator();
127                g.setType(type);
128                // SPRING PATCH BEGIN
129                g.setContextClass(type);
130                // SPRING PATCH END
131                g.setClassLoader(ci.c2.getClassLoader());
132                g.setNamingPolicy(ci.namingPolicy);
133                g.setStrategy(ci.strategy);
134                g.setAttemptLoad(ci.attemptLoad);
135                return g.create();
136        }
137
138        private MethodProxy() {
139        }
140
141        /**
142         * Return the signature of the proxied method.
143         */
144        public Signature getSignature() {
145                return sig1;
146        }
147
148        /**
149         * Return the name of the synthetic method created by CGLIB which is
150         * used by {@link #invokeSuper} to invoke the superclass
151         * (non-intercepted) method implementation. The parameter types are
152         * the same as the proxied method.
153         */
154        public String getSuperName() {
155                return sig2.getName();
156        }
157
158        /**
159         * Return the {@link org.springframework.cglib.reflect.FastClass} method index
160         * for the method used by {@link #invokeSuper}. This index uniquely
161         * identifies the method within the generated proxy, and therefore
162         * can be useful to reference external metadata.
163         * @see #getSuperName
164         */
165        public int getSuperIndex() {
166                init();
167                return fastClassInfo.i2;
168        }
169
170        // For testing
171        FastClass getFastClass() {
172                init();
173                return fastClassInfo.f1;
174        }
175
176        // For testing
177        FastClass getSuperFastClass() {
178                init();
179                return fastClassInfo.f2;
180        }
181
182        /**
183         * Return the <code>MethodProxy</code> used when intercepting the method
184         * matching the given signature.
185         * @param type the class generated by Enhancer
186         * @param sig the signature to match
187         * @return the MethodProxy instance, or null if no applicable matching method is found
188         * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
189         */
190        public static MethodProxy find(Class type, Signature sig) {
191                try {
192                        Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
193                                        MethodInterceptorGenerator.FIND_PROXY_TYPES);
194                        return (MethodProxy) m.invoke(null, new Object[]{sig});
195                }
196                catch (NoSuchMethodException ex) {
197                        throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
198                }
199                catch (IllegalAccessException | InvocationTargetException ex) {
200                        throw new CodeGenerationException(ex);
201                }
202        }
203
204        /**
205         * Invoke the original method, on a different object of the same type.
206         * @param obj the compatible object; recursion will result if you use the object passed as the first
207         * argument to the MethodInterceptor (usually not what you want)
208         * @param args the arguments passed to the intercepted method; you may substitute a different
209         * argument array as long as the types are compatible
210         * @throws Throwable the bare exceptions thrown by the called method are passed through
211         * without wrapping in an <code>InvocationTargetException</code>
212         * @see MethodInterceptor#intercept
213         */
214        public Object invoke(Object obj, Object[] args) throws Throwable {
215                try {
216                        init();
217                        FastClassInfo fci = fastClassInfo;
218                        return fci.f1.invoke(fci.i1, obj, args);
219                }
220                catch (InvocationTargetException ex) {
221                        throw ex.getTargetException();
222                }
223                catch (IllegalArgumentException ex) {
224                        if (fastClassInfo.i1 < 0)
225                                throw new IllegalArgumentException("Protected method: " + sig1);
226                        throw ex;
227                }
228        }
229
230        /**
231         * Invoke the original (super) method on the specified object.
232         * @param obj the enhanced object, must be the object passed as the first
233         * argument to the MethodInterceptor
234         * @param args the arguments passed to the intercepted method; you may substitute a different
235         * argument array as long as the types are compatible
236         * @throws Throwable the bare exceptions thrown by the called method are passed through
237         * without wrapping in an <code>InvocationTargetException</code>
238         * @see MethodInterceptor#intercept
239         */
240        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
241                try {
242                        init();
243                        FastClassInfo fci = fastClassInfo;
244                        return fci.f2.invoke(fci.i2, obj, args);
245                }
246                catch (InvocationTargetException e) {
247                        throw e.getTargetException();
248                }
249        }
250
251}