All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ext.test4j.cglib.proxy.MethodProxy Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
/*
 * Copyright 2003,2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ext.test4j.cglib.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import ext.test4j.cglib.core.AbstractClassGenerator;
import ext.test4j.cglib.core.CodeGenerationException;
import ext.test4j.cglib.core.GeneratorStrategy;
import ext.test4j.cglib.core.NamingPolicy;
import ext.test4j.cglib.core.Signature;
import ext.test4j.cglib.reflect.FastClass;

/**
 * Classes generated by {@link Enhancer} pass this object to the registered
 * {@link MethodInterceptor} objects when an intercepted method is invoked. It
 * can be used to either invoke the original method, or call the same method on
 * a different object of the same type.
 * 
 * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class MethodProxy {
	private Signature sig1;
	private Signature sig2;
	private CreateInfo createInfo;

	private final Object initLock = new Object();
	private volatile FastClassInfo fastClassInfo;

	/**
	 * For internal use by {@link Enhancer} only; see the
	 * {@link ext.test4j.cglib.reflect.FastMethod} class for similar
	 * functionality.
	 */
	public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
		MethodProxy proxy = new MethodProxy();
		proxy.sig1 = new Signature(name1, desc);
		proxy.sig2 = new Signature(name2, desc);
		proxy.createInfo = new CreateInfo(c1, c2);
		return proxy;
	}

	private void init() {
		/*
		 * Using a volatile invariant allows us to initialize the FastClass and
		 * method index pairs atomically.
		 * 
		 * Double-checked locking is safe with volatile in Java 5. Before 1.5
		 * this code could allow fastClassInfo to be instantiated more than
		 * once, which appears to be benign.
		 */
		if (fastClassInfo == null) {
			synchronized (initLock) {
				if (fastClassInfo == null) {
					CreateInfo ci = createInfo;

					FastClassInfo fci = new FastClassInfo();
					fci.f1 = helper(ci, ci.c1);
					fci.f2 = helper(ci, ci.c2);
					fci.i1 = fci.f1.getIndex(sig1);
					fci.i2 = fci.f2.getIndex(sig2);
					fastClassInfo = fci;
					createInfo = null;
				}
			}
		}
	}

	private static class FastClassInfo {
		FastClass f1;
		FastClass f2;
		int i1;
		int i2;
	}

	private static class CreateInfo {
		Class c1;
		Class c2;
		NamingPolicy namingPolicy;
		GeneratorStrategy strategy;
		boolean attemptLoad;

		public CreateInfo(Class c1, Class c2) {
			this.c1 = c1;
			this.c2 = c2;
			AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
			if (fromEnhancer != null) {
				namingPolicy = fromEnhancer.getNamingPolicy();
				strategy = fromEnhancer.getStrategy();
				attemptLoad = fromEnhancer.getAttemptLoad();
			}
		}
	}

	private static FastClass helper(CreateInfo ci, Class type) {
		FastClass.Generator g = new FastClass.Generator();
		g.setType(type);
		g.setClassLoader(ci.c2.getClassLoader());
		g.setNamingPolicy(ci.namingPolicy);
		g.setStrategy(ci.strategy);
		g.setAttemptLoad(ci.attemptLoad);
		return g.create();
	}

	private MethodProxy() {
	}

	/**
	 * Return the signature of the proxied method.
	 */
	public Signature getSignature() {
		return sig1;
	}

	/**
	 * Return the name of the synthetic method created by CGLIB which is used by
	 * {@link #invokeSuper} to invoke the superclass (non-intercepted) method
	 * implementation. The parameter types are the same as the proxied method.
	 */
	public String getSuperName() {
		return sig2.getName();
	}

	/**
	 * Return the {@link ext.test4j.cglib.reflect.FastClass} method index for
	 * the method used by {@link #invokeSuper}. This index uniquely identifies
	 * the method within the generated proxy, and therefore can be useful to
	 * reference external metadata.
	 * 
	 * @see #getSuperName
	 */
	public int getSuperIndex() {
		init();
		return fastClassInfo.i2;
	}

	// For testing
	FastClass getFastClass() {
		init();
		return fastClassInfo.f1;
	}

	// For testing
	FastClass getSuperFastClass() {
		init();
		return fastClassInfo.f2;
	}

	/**
	 * Return the MethodProxy used when intercepting the method
	 * matching the given signature.
	 * 
	 * @param type
	 *            the class generated by Enhancer
	 * @param sig
	 *            the signature to match
	 * @return the MethodProxy instance, or null if no applicable matching
	 *         method is found
	 * @throws IllegalArgumentException
	 *             if the Class was not created by Enhancer or does not use a
	 *             MethodInterceptor
	 */
	public static MethodProxy find(Class type, Signature sig) {
		try {
			Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
					MethodInterceptorGenerator.FIND_PROXY_TYPES);
			return (MethodProxy) m.invoke(null, new Object[] { sig });
		} catch (NoSuchMethodException e) {
			throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
		} catch (IllegalAccessException e) {
			throw new CodeGenerationException(e);
		} catch (InvocationTargetException e) {
			throw new CodeGenerationException(e);
		}
	}

	/**
	 * Invoke the original method, on a different object of the same type.
	 * 
	 * @param obj
	 *            the compatible object; recursion will result if you use the
	 *            object passed as the first argument to the MethodInterceptor
	 *            (usually not what you want)
	 * @param args
	 *            the arguments passed to the intercepted method; you may
	 *            substitute a different argument array as long as the types are
	 *            compatible
	 * @see MethodInterceptor#intercept
	 * @throws Throwable
	 *             the bare exceptions thrown by the called method are passed
	 *             through without wrapping in an
	 *             InvocationTargetException
	 */
	public Object invoke(Object obj, Object[] args) throws Throwable {
		try {
			init();
			FastClassInfo fci = fastClassInfo;
			return fci.f1.invoke(fci.i1, obj, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		} catch (IllegalArgumentException e) {
			if (fastClassInfo.i1 < 0)
				throw new IllegalArgumentException("Protected method: " + sig1);
			throw e;
		}
	}

	/**
	 * Invoke the original (super) method on the specified object.
	 * 
	 * @param obj
	 *            the enhanced object, must be the object passed as the first
	 *            argument to the MethodInterceptor
	 * @param args
	 *            the arguments passed to the intercepted method; you may
	 *            substitute a different argument array as long as the types are
	 *            compatible
	 * @see MethodInterceptor#intercept
	 * @throws Throwable
	 *             the bare exceptions thrown by the called method are passed
	 *             through without wrapping in an
	 *             InvocationTargetException
	 */
	public Object invokeSuper(Object obj, Object[] args) throws Throwable {
		try {
			init();
			FastClassInfo fci = fastClassInfo;
			return fci.f2.invoke(fci.i2, obj, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy