0001: /*
0002: * Copyright 2002,2003,2004 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package net.sf.cglib.proxy;
0017:
0018: import java.lang.reflect.Constructor;
0019: import java.lang.reflect.InvocationTargetException;
0020: import java.lang.reflect.Method;
0021: import java.util.*;
0022: import net.sf.cglib.core.*;
0023: import org.objectweb.asm.Attribute;
0024: import org.objectweb.asm.ClassVisitor;
0025: import org.objectweb.asm.Type;
0026: import org.objectweb.asm.Label;
0027:
0028: /**
0029: * Generates dynamic subclasses to enable method interception. This
0030: * class started as a substitute for the standard Dynamic Proxy support
0031: * included with JDK 1.3, but one that allowed the proxies to extend a
0032: * concrete base class, in addition to implementing interfaces. The dynamically
0033: * generated subclasses override the non-final methods of the superclass and
0034: * have hooks which callback to user-defined interceptor
0035: * implementations.
0036: * <p>
0037: * The original and most general callback type is the {@link MethodInterceptor}, which
0038: * in AOP terms enables "around advice"--that is, you can invoke custom code both before
0039: * and after the invocation of the "super" method. In addition you can modify the
0040: * arguments before calling the super method, or not call it at all.
0041: * <p>
0042: * Although <code>MethodInterceptor</code> is generic enough to meet any
0043: * interception need, it is often overkill. For simplicity and performance, additional
0044: * specialized callback types, such as {@link LazyLoader} are also available.
0045: * Often a single callback will be used per enhanced class, but you can control
0046: * which callback is used on a per-method basis with a {@link CallbackFilter}.
0047: * <p>
0048: * The most common uses of this class are embodied in the static helper methods. For
0049: * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
0050: * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
0051: * <p>
0052: * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
0053: * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
0054: * to change the callbacks of an existing object, as well as a faster and easier way to create
0055: * new instances of the same type.
0056: * <p>
0057: * For an almost drop-in replacement for
0058: * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
0059: */
0060: public class Enhancer extends AbstractClassGenerator {
0061: private static final CallbackFilter ALL_ZERO = new CallbackFilter() {
0062: public int accept(Method method) {
0063: return 0;
0064: }
0065: };
0066:
0067: private static final Source SOURCE = new Source(Enhancer.class
0068: .getName());
0069: private static final EnhancerKey KEY_FACTORY = (EnhancerKey) KeyFactory
0070: .create(EnhancerKey.class);
0071:
0072: private static final String BOUND_FIELD = "CGLIB$BOUND";
0073: private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
0074: private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
0075: private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
0076: private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
0077: private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
0078:
0079: private static final Type FACTORY = TypeUtils
0080: .parseType("net.sf.cglib.proxy.Factory");
0081: private static final Type ILLEGAL_STATE_EXCEPTION = TypeUtils
0082: .parseType("IllegalStateException");
0083: private static final Type ILLEGAL_ARGUMENT_EXCEPTION = TypeUtils
0084: .parseType("IllegalArgumentException");
0085: private static final Type THREAD_LOCAL = TypeUtils
0086: .parseType("ThreadLocal");
0087: private static final Type CALLBACK = TypeUtils
0088: .parseType("net.sf.cglib.proxy.Callback");
0089: private static final Type CALLBACK_ARRAY = TypeUtils
0090: .parseType("net.sf.cglib.proxy.Callback[]");
0091: private static final Signature CSTRUCT_NULL = TypeUtils
0092: .parseConstructor("");
0093: private static final Signature SET_THREAD_CALLBACKS = new Signature(
0094: SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE,
0095: new Type[] { CALLBACK_ARRAY });
0096: private static final Signature SET_STATIC_CALLBACKS = new Signature(
0097: SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE,
0098: new Type[] { CALLBACK_ARRAY });
0099: private static final Signature NEW_INSTANCE = new Signature(
0100: "newInstance", Constants.TYPE_OBJECT,
0101: new Type[] { CALLBACK_ARRAY });
0102: private static final Signature MULTIARG_NEW_INSTANCE = new Signature(
0103: "newInstance", Constants.TYPE_OBJECT, new Type[] {
0104: Constants.TYPE_CLASS_ARRAY,
0105: Constants.TYPE_OBJECT_ARRAY, CALLBACK_ARRAY, });
0106: private static final Signature SINGLE_NEW_INSTANCE = new Signature(
0107: "newInstance", Constants.TYPE_OBJECT,
0108: new Type[] { CALLBACK });
0109: private static final Signature SET_CALLBACK = new Signature(
0110: "setCallback", Type.VOID_TYPE, new Type[] { Type.INT_TYPE,
0111: CALLBACK });
0112: private static final Signature GET_CALLBACK = new Signature(
0113: "getCallback", CALLBACK, new Type[] { Type.INT_TYPE });
0114: private static final Signature SET_CALLBACKS = new Signature(
0115: "setCallbacks", Type.VOID_TYPE,
0116: new Type[] { CALLBACK_ARRAY });
0117: private static final Signature GET_CALLBACKS = new Signature(
0118: "getCallbacks", CALLBACK_ARRAY, new Type[0]);
0119: private static final Signature THREAD_LOCAL_GET = TypeUtils
0120: .parseSignature("Object get()");
0121: private static final Signature THREAD_LOCAL_SET = TypeUtils
0122: .parseSignature("void set(Object)");
0123: private static final Signature BIND_CALLBACKS = TypeUtils
0124: .parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
0125:
0126: /** Internal interface, only public due to ClassLoader issues. */
0127: public interface EnhancerKey {
0128: public Object newInstance(String type, String[] interfaces,
0129: CallbackFilter filter, Type[] callbackTypes,
0130: boolean useFactory,
0131: boolean interceptDuringConstruction,
0132: Long serialVersionUID);
0133: }
0134:
0135: private Class[] interfaces;
0136: private CallbackFilter filter;
0137: private Callback[] callbacks;
0138: private Type[] callbackTypes;
0139: private boolean classOnly;
0140: private Class super class;
0141: private Class[] argumentTypes;
0142: private Object[] arguments;
0143: private boolean useFactory = true;
0144: private Long serialVersionUID;
0145: private boolean interceptDuringConstruction = true;
0146:
0147: /**
0148: * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
0149: * object should be used for each generated object, and should not
0150: * be shared across threads. To create additional instances of a
0151: * generated class, use the <code>Factory</code> interface.
0152: * @see Factory
0153: */
0154: public Enhancer() {
0155: super (SOURCE);
0156: }
0157:
0158: /**
0159: * Set the class which the generated class will extend. As a convenience,
0160: * if the supplied superclass is actually an interface, <code>setInterfaces</code>
0161: * will be called with the appropriate argument instead.
0162: * A non-interface argument must not be declared as final, and must have an
0163: * accessible constructor.
0164: * @param superclass class to extend or interface to implement
0165: * @see #setInterfaces(Class[])
0166: */
0167: public void setSuperclass(Class super class) {
0168: if (super class != null && super class.isInterface()) {
0169: setInterfaces(new Class[] { super class });
0170: } else if (super class != null
0171: && super class.equals(Object.class)) {
0172: // affects choice of ClassLoader
0173: this .super class = null;
0174: } else {
0175: this .super class = super class;
0176: }
0177: }
0178:
0179: /**
0180: * Set the interfaces to implement. The <code>Factory</code> interface will
0181: * always be implemented regardless of what is specified here.
0182: * @param interfaces array of interfaces to implement, or null
0183: * @see Factory
0184: */
0185: public void setInterfaces(Class[] interfaces) {
0186: this .interfaces = interfaces;
0187: }
0188:
0189: /**
0190: * Set the {@link CallbackFilter} used to map the generated class' methods
0191: * to a particular callback index.
0192: * New object instances will always use the same mapping, but may use different
0193: * actual callback objects.
0194: * @param filter the callback filter to use when generating a new class
0195: * @see #setCallbacks
0196: */
0197: public void setCallbackFilter(CallbackFilter filter) {
0198: this .filter = filter;
0199: }
0200:
0201: /**
0202: * Set the single {@link Callback} to use.
0203: * Ignored if you use {@link #createClass}.
0204: * @param callback the callback to use for all methods
0205: * @see #setCallbacks
0206: */
0207: public void setCallback(final Callback callback) {
0208: setCallbacks(new Callback[] { callback });
0209: }
0210:
0211: /**
0212: * Set the array of callbacks to use.
0213: * Ignored if you use {@link #createClass}.
0214: * You must use a {@link CallbackFilter} to specify the index into this
0215: * array for each method in the proxied class.
0216: * @param callbacks the callback array
0217: * @see #setCallbackFilter
0218: * @see #setCallback
0219: */
0220: public void setCallbacks(Callback[] callbacks) {
0221: if (callbacks != null && callbacks.length == 0) {
0222: throw new IllegalArgumentException("Array cannot be empty");
0223: }
0224: this .callbacks = callbacks;
0225: }
0226:
0227: /**
0228: * Set whether the enhanced object instances should implement
0229: * the {@link Factory} interface.
0230: * This was added for tools that need for proxies to be more
0231: * indistinguishable from their targets. Also, in some cases it may
0232: * be necessary to disable the <code>Factory</code> interface to
0233: * prevent code from changing the underlying callbacks.
0234: * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
0235: */
0236: public void setUseFactory(boolean useFactory) {
0237: this .useFactory = useFactory;
0238: }
0239:
0240: /**
0241: * Set whether methods called from within the proxy's constructer
0242: * will be intercepted. The default value is true. Unintercepted methods
0243: * will call the method of the proxy's base class, if it exists.
0244: * @param interceptDuringConstruction whether to intercept methods called from the constructor
0245: */
0246: public void setInterceptDuringConstruction(
0247: boolean interceptDuringConstruction) {
0248: this .interceptDuringConstruction = interceptDuringConstruction;
0249: }
0250:
0251: /**
0252: * Set the single type of {@link Callback} to use.
0253: * This may be used instead of {@link #setCallback} when calling
0254: * {@link #createClass}, since it may not be possible to have
0255: * an array of actual callback instances.
0256: * @param callbackType the type of callback to use for all methods
0257: * @see #setCallbackTypes
0258: */
0259: public void setCallbackType(Class callbackType) {
0260: setCallbackTypes(new Class[] { callbackType });
0261: }
0262:
0263: /**
0264: * Set the array of callback types to use.
0265: * This may be used instead of {@link #setCallbacks} when calling
0266: * {@link #createClass}, since it may not be possible to have
0267: * an array of actual callback instances.
0268: * You must use a {@link CallbackFilter} to specify the index into this
0269: * array for each method in the proxied class.
0270: * @param callbackTypes the array of callback types
0271: */
0272: public void setCallbackTypes(Class[] callbackTypes) {
0273: if (callbackTypes != null && callbackTypes.length == 0) {
0274: throw new IllegalArgumentException("Array cannot be empty");
0275: }
0276: this .callbackTypes = CallbackInfo.determineTypes(callbackTypes);
0277: }
0278:
0279: /**
0280: * Generate a new class if necessary and uses the specified
0281: * callbacks (if any) to create a new object instance.
0282: * Uses the no-arg constructor of the superclass.
0283: * @return a new instance
0284: */
0285: public Object create() {
0286: classOnly = false;
0287: argumentTypes = null;
0288: return createHelper();
0289: }
0290:
0291: /**
0292: * Generate a new class if necessary and uses the specified
0293: * callbacks (if any) to create a new object instance.
0294: * Uses the constructor of the superclass matching the <code>argumentTypes</code>
0295: * parameter, with the given arguments.
0296: * @param argumentTypes constructor signature
0297: * @param arguments compatible wrapped arguments to pass to constructor
0298: * @return a new instance
0299: */
0300: public Object create(Class[] argumentTypes, Object[] arguments) {
0301: classOnly = false;
0302: if (argumentTypes == null || arguments == null
0303: || argumentTypes.length != arguments.length) {
0304: throw new IllegalArgumentException(
0305: "Arguments must be non-null and of equal length");
0306: }
0307: this .argumentTypes = argumentTypes;
0308: this .arguments = arguments;
0309: return createHelper();
0310: }
0311:
0312: /**
0313: * Generate a new class if necessary and return it without creating a new instance.
0314: * This ignores any callbacks that have been set.
0315: * To create a new instance you will have to use reflection, and methods
0316: * called during the constructor will not be intercepted. To avoid this problem,
0317: * use the multi-arg <code>create</code> method.
0318: * @see #create(Class[], Object[])
0319: */
0320: public Class createClass() {
0321: classOnly = true;
0322: return (Class) createHelper();
0323: }
0324:
0325: /**
0326: * Insert a static serialVersionUID field into the generated class.
0327: * @param sUID the field value, or null to avoid generating field.
0328: */
0329: public void setSerialVersionUID(Long sUID) {
0330: serialVersionUID = sUID;
0331: }
0332:
0333: private void validate() {
0334: if (classOnly ^ (callbacks == null)) {
0335: if (classOnly) {
0336: throw new IllegalStateException(
0337: "createClass does not accept callbacks");
0338: } else {
0339: throw new IllegalStateException(
0340: "Callbacks are required");
0341: }
0342: }
0343: if (classOnly && (callbackTypes == null)) {
0344: throw new IllegalStateException(
0345: "Callback types are required");
0346: }
0347: if (callbacks != null && callbackTypes != null) {
0348: if (callbacks.length != callbackTypes.length) {
0349: throw new IllegalStateException(
0350: "Lengths of callback and callback types array must be the same");
0351: }
0352: Type[] check = CallbackInfo.determineTypes(callbacks);
0353: for (int i = 0; i < check.length; i++) {
0354: if (!check[i].equals(callbackTypes[i])) {
0355: throw new IllegalStateException("Callback "
0356: + check[i] + " is not assignable to "
0357: + callbackTypes[i]);
0358: }
0359: }
0360: } else if (callbacks != null) {
0361: callbackTypes = CallbackInfo.determineTypes(callbacks);
0362: }
0363: if (filter == null) {
0364: if (callbackTypes.length > 1) {
0365: throw new IllegalStateException(
0366: "Multiple callback types possible but no filter specified");
0367: }
0368: filter = ALL_ZERO;
0369: }
0370: if (interfaces != null) {
0371: for (int i = 0; i < interfaces.length; i++) {
0372: if (interfaces[i] == null) {
0373: throw new IllegalStateException(
0374: "Interfaces cannot be null");
0375: }
0376: if (!interfaces[i].isInterface()) {
0377: throw new IllegalStateException(interfaces[i]
0378: + " is not an interface");
0379: }
0380: }
0381: }
0382: }
0383:
0384: private Object createHelper() {
0385: validate();
0386: if (super class != null) {
0387: setNamePrefix(super class.getName());
0388: } else if (interfaces != null) {
0389: setNamePrefix(interfaces[ReflectUtils
0390: .findPackageProtected(interfaces)].getName());
0391: }
0392: return super .create(KEY_FACTORY.newInstance(
0393: (super class != null) ? super class.getName() : null,
0394: ReflectUtils.getNames(interfaces), filter,
0395: callbackTypes, useFactory, interceptDuringConstruction,
0396: serialVersionUID));
0397: }
0398:
0399: protected ClassLoader getDefaultClassLoader() {
0400: if (super class != null) {
0401: return super class.getClassLoader();
0402: } else if (interfaces != null) {
0403: return interfaces[0].getClassLoader();
0404: } else {
0405: return null;
0406: }
0407: }
0408:
0409: private Signature rename(Signature sig, int index) {
0410: return new Signature("CGLIB$" + sig.getName() + "$" + index,
0411: sig.getDescriptor());
0412: }
0413:
0414: /**
0415: * Finds all of the methods that will be extended by an
0416: * Enhancer-generated class using the specified superclass and
0417: * interfaces. This can be useful in building a list of Callback
0418: * objects. The methods are added to the end of the given list. Due
0419: * to the subclassing nature of the classes generated by Enhancer,
0420: * the methods are guaranteed to be non-static, non-final, and
0421: * non-private. Each method signature will only occur once, even if
0422: * it occurs in multiple classes.
0423: * @param superclass the class that will be extended, or null
0424: * @param interfaces the list of interfaces that will be implemented, or null
0425: * @param methods the list into which to copy the applicable methods
0426: */
0427: public static void getMethods(Class super class, Class[] interfaces,
0428: List methods) {
0429: getMethods(super class, interfaces, methods, null, null);
0430: }
0431:
0432: private static void getMethods(Class super class,
0433: Class[] interfaces, List methods, List interfaceMethods,
0434: Set forcePublic) {
0435: ReflectUtils.addAllMethods(super class, methods);
0436: List target = (interfaceMethods != null) ? interfaceMethods
0437: : methods;
0438: if (interfaces != null) {
0439: for (int i = 0; i < interfaces.length; i++) {
0440: if (interfaces[i] != Factory.class) {
0441: ReflectUtils.addAllMethods(interfaces[i], target);
0442: }
0443: }
0444: }
0445: if (interfaceMethods != null) {
0446: if (forcePublic != null) {
0447: forcePublic.addAll(MethodWrapper
0448: .createSet(interfaceMethods));
0449: }
0450: methods.addAll(interfaceMethods);
0451: }
0452: CollectionUtils.filter(methods, new RejectModifierPredicate(
0453: Constants.ACC_STATIC));
0454: CollectionUtils.filter(methods, new VisibilityPredicate(
0455: super class, true));
0456: CollectionUtils.filter(methods, new DuplicatesPredicate());
0457: CollectionUtils.filter(methods, new RejectModifierPredicate(
0458: Constants.ACC_FINAL));
0459: }
0460:
0461: public void generateClass(ClassVisitor v) throws Exception {
0462: Class sc = (super class == null) ? Object.class : super class;
0463:
0464: if (TypeUtils.isFinal(sc.getModifiers()))
0465: throw new IllegalArgumentException(
0466: "Cannot subclass final class " + sc);
0467: List constructors = new ArrayList(Arrays.asList(sc
0468: .getDeclaredConstructors()));
0469: filterConstructors(sc, constructors);
0470:
0471: // Order is very important: must add superclass, then
0472: // its superclass chain, then each interface and
0473: // its superinterfaces.
0474: List actualMethods = new ArrayList();
0475: List interfaceMethods = new ArrayList();
0476: final Set forcePublic = new HashSet();
0477: getMethods(sc, interfaces, actualMethods, interfaceMethods,
0478: forcePublic);
0479:
0480: List methods = CollectionUtils.transform(actualMethods,
0481: new Transformer() {
0482: public Object transform(Object value) {
0483: Method method = (Method) value;
0484: int modifiers = Constants.ACC_FINAL
0485: | (method.getModifiers()
0486: & ~Constants.ACC_ABSTRACT
0487: & ~Constants.ACC_NATIVE & ~Constants.ACC_SYNCHRONIZED);
0488: if (forcePublic.contains(MethodWrapper
0489: .create(method))) {
0490: modifiers = (modifiers & ~Constants.ACC_PROTECTED)
0491: | Constants.ACC_PUBLIC;
0492: }
0493: return ReflectUtils.getMethodInfo(method,
0494: modifiers);
0495: }
0496: });
0497:
0498: ClassEmitter e = new ClassEmitter(v);
0499: e.begin_class(Constants.V1_2, Constants.ACC_PUBLIC,
0500: getClassName(), Type.getType(sc),
0501: (useFactory ? TypeUtils.add(TypeUtils
0502: .getTypes(interfaces), FACTORY) : TypeUtils
0503: .getTypes(interfaces)), Constants.SOURCE_FILE);
0504: List constructorInfo = CollectionUtils.transform(constructors,
0505: MethodInfoTransformer.getInstance());
0506:
0507: e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD,
0508: Type.BOOLEAN_TYPE, null);
0509: if (!interceptDuringConstruction) {
0510: e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD,
0511: Type.BOOLEAN_TYPE, null);
0512: }
0513: e.declare_field(Constants.PRIVATE_FINAL_STATIC,
0514: THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
0515: e.declare_field(Constants.PRIVATE_FINAL_STATIC,
0516: STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
0517: if (serialVersionUID != null) {
0518: e.declare_field(Constants.PRIVATE_FINAL_STATIC,
0519: Constants.SUID_FIELD_NAME, Type.LONG_TYPE,
0520: serialVersionUID);
0521: }
0522:
0523: for (int i = 0; i < callbackTypes.length; i++) {
0524: e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i),
0525: callbackTypes[i], null);
0526: }
0527:
0528: emitMethods(e, methods, actualMethods);
0529: emitConstructors(e, constructorInfo);
0530: emitSetThreadCallbacks(e);
0531: emitSetStaticCallbacks(e);
0532: emitBindCallbacks(e);
0533:
0534: if (useFactory) {
0535: int[] keys = getCallbackKeys();
0536: emitNewInstanceCallbacks(e);
0537: emitNewInstanceCallback(e);
0538: emitNewInstanceMultiarg(e, constructorInfo);
0539: emitGetCallback(e, keys);
0540: emitSetCallback(e, keys);
0541: emitGetCallbacks(e);
0542: emitSetCallbacks(e);
0543: }
0544:
0545: e.end_class();
0546: }
0547:
0548: /**
0549: * Filter the list of constructors from the superclass. The
0550: * constructors which remain will be included in the generated
0551: * class. The default implementation is to filter out all private
0552: * constructors, but subclasses may extend Enhancer to override this
0553: * behavior.
0554: * @param sc the superclass
0555: * @param constructors the list of all declared constructors from the superclass
0556: * @throws IllegalArgumentException if there are no non-private constructors
0557: */
0558: protected void filterConstructors(Class sc, List constructors) {
0559: CollectionUtils.filter(constructors, new VisibilityPredicate(
0560: sc, true));
0561: if (constructors.size() == 0)
0562: throw new IllegalArgumentException(
0563: "No visible constructors in " + sc);
0564: }
0565:
0566: protected Object firstInstance(Class type) throws Exception {
0567: if (classOnly) {
0568: return type;
0569: } else {
0570: return createUsingReflection(type);
0571: }
0572: }
0573:
0574: protected Object nextInstance(Object instance) {
0575: Class protoclass = (instance instanceof Class) ? (Class) instance
0576: : instance.getClass();
0577: if (classOnly) {
0578: return protoclass;
0579: } else if (instance instanceof Factory) {
0580: if (argumentTypes != null) {
0581: return ((Factory) instance).newInstance(argumentTypes,
0582: arguments, callbacks);
0583: } else {
0584: return ((Factory) instance).newInstance(callbacks);
0585: }
0586: } else {
0587: return createUsingReflection(protoclass);
0588: }
0589: }
0590:
0591: /**
0592: * Call this method to register the {@link Callback} array to use before
0593: * creating a new instance of the generated class via reflection. If you are using
0594: * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
0595: * new instances, this method is unnecessary. Its primary use is for when you want to
0596: * cache and reuse a generated class yourself, and the generated class does
0597: * <i>not</i> implement the {@link Factory} interface.
0598: * <p>
0599: * Note that this method only registers the callbacks on the current thread.
0600: * If you want to register callbacks for instances created by multiple threads,
0601: * use {@link #registerStaticCallbacks}.
0602: * <p>
0603: * The registered callbacks are overwritten and subsequently cleared
0604: * when calling any of the <code>create</code> methods (such as
0605: * {@link #create}).
0606: * @param generatedClass a class previously created by {@link Enhancer}
0607: * @param callbacks the array of callbacks to use when instances of the generated
0608: * class are created
0609: * @see #setUseFactory
0610: */
0611: public static void registerCallbacks(Class generatedClass,
0612: Callback[] callbacks) {
0613: setThreadCallbacks(generatedClass, callbacks);
0614: }
0615:
0616: /**
0617: * Similar to {@link #registerCallbacks}, but suitable for use
0618: * when multiple threads will be creating instances of the generated class.
0619: * The thread-level callbacks will always override the static callbacks.
0620: * Static callbacks are never cleared.
0621: * @param generatedClass a class previously created by {@link Enhancer}
0622: * @param callbacks the array of callbacks to use when instances of the generated
0623: * class are created
0624: */
0625: public static void registerStaticCallbacks(Class generatedClass,
0626: Callback[] callbacks) {
0627: setCallbacksHelper(generatedClass, callbacks,
0628: SET_STATIC_CALLBACKS_NAME);
0629: }
0630:
0631: /**
0632: * Determine if a class was generated using <code>Enhancer</code>.
0633: * @param type any class
0634: * @return whether the class was generated using <code>Enhancer</code>
0635: */
0636: public static boolean isEnhanced(Class type) {
0637: try {
0638: getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
0639: return true;
0640: } catch (NoSuchMethodException e) {
0641: return false;
0642: }
0643: }
0644:
0645: private static void setThreadCallbacks(Class type,
0646: Callback[] callbacks) {
0647: setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
0648: }
0649:
0650: private static void setCallbacksHelper(Class type,
0651: Callback[] callbacks, String methodName) {
0652: // TODO: optimize
0653: try {
0654: Method setter = getCallbacksSetter(type, methodName);
0655: setter.invoke(null, new Object[] { callbacks });
0656: } catch (NoSuchMethodException e) {
0657: throw new IllegalArgumentException(type
0658: + " is not an enhanced class");
0659: } catch (IllegalAccessException e) {
0660: throw new CodeGenerationException(e);
0661: } catch (InvocationTargetException e) {
0662: throw new CodeGenerationException(e);
0663: }
0664: }
0665:
0666: private static Method getCallbacksSetter(Class type,
0667: String methodName) throws NoSuchMethodException {
0668: return type.getDeclaredMethod(methodName,
0669: new Class[] { Callback[].class });
0670: }
0671:
0672: private Object createUsingReflection(Class type) {
0673: setThreadCallbacks(type, callbacks);
0674: try {
0675:
0676: if (argumentTypes != null) {
0677:
0678: return ReflectUtils.newInstance(type, argumentTypes,
0679: arguments);
0680:
0681: } else {
0682:
0683: return ReflectUtils.newInstance(type);
0684:
0685: }
0686: } finally {
0687: // clear thread callbacks to allow them to be gc'd
0688: setThreadCallbacks(type, null);
0689: }
0690: }
0691:
0692: /**
0693: * Helper method to create an intercepted object.
0694: * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
0695: * instead of this static method.
0696: * @param type class to extend or interface to implement
0697: * @param callback the callback to use for all methods
0698: */
0699: public static Object create(Class type, Callback callback) {
0700: Enhancer e = new Enhancer();
0701: e.setSuperclass(type);
0702: e.setCallback(callback);
0703: return e.create();
0704: }
0705:
0706: /**
0707: * Helper method to create an intercepted object.
0708: * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
0709: * instead of this static method.
0710: * @param type class to extend or interface to implement
0711: * @param interfaces array of interfaces to implement, or null
0712: * @param callback the callback to use for all methods
0713: */
0714: public static Object create(Class super class, Class interfaces[],
0715: Callback callback) {
0716: Enhancer e = new Enhancer();
0717: e.setSuperclass(super class);
0718: e.setInterfaces(interfaces);
0719: e.setCallback(callback);
0720: return e.create();
0721: }
0722:
0723: /**
0724: * Helper method to create an intercepted object.
0725: * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
0726: * instead of this static method.
0727: * @param type class to extend or interface to implement
0728: * @param interfaces array of interfaces to implement, or null
0729: * @param filter the callback filter to use when generating a new class
0730: * @param callbacks callback implementations to use for the enhanced object
0731: */
0732: public static Object create(Class super class, Class[] interfaces,
0733: CallbackFilter filter, Callback[] callbacks) {
0734: Enhancer e = new Enhancer();
0735: e.setSuperclass(super class);
0736: e.setInterfaces(interfaces);
0737: e.setCallbackFilter(filter);
0738: e.setCallbacks(callbacks);
0739: return e.create();
0740: }
0741:
0742: private void emitConstructors(ClassEmitter ce, List constructors) {
0743: boolean seenNull = false;
0744: for (Iterator it = constructors.iterator(); it.hasNext();) {
0745: MethodInfo constructor = (MethodInfo) it.next();
0746: CodeEmitter e = EmitUtils.begin_method(ce, constructor,
0747: Constants.ACC_PUBLIC);
0748: e.load_this ();
0749: e.dup();
0750: e.load_args();
0751: Signature sig = constructor.getSignature();
0752: seenNull = seenNull || sig.getDescriptor().equals("()V");
0753: e.super _invoke_constructor(sig);
0754: e.invoke_static_this (BIND_CALLBACKS);
0755: if (!interceptDuringConstruction) {
0756: e.load_this ();
0757: e.push(1);
0758: e.putfield(CONSTRUCTED_FIELD);
0759: }
0760: e.return_value();
0761: e.end_method();
0762: }
0763: if (!classOnly && !seenNull && arguments == null)
0764: throw new IllegalArgumentException(
0765: "Superclass has no null constructors but no arguments were given");
0766: }
0767:
0768: private int[] getCallbackKeys() {
0769: int[] keys = new int[callbackTypes.length];
0770: for (int i = 0; i < callbackTypes.length; i++) {
0771: keys[i] = i;
0772: }
0773: return keys;
0774: }
0775:
0776: private void emitGetCallback(ClassEmitter ce, int[] keys) {
0777: final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0778: GET_CALLBACK, null);
0779: e.load_this ();
0780: e.invoke_static_this (BIND_CALLBACKS);
0781: e.load_this ();
0782: e.load_arg(0);
0783: e.process_switch(keys, new ProcessSwitchCallback() {
0784: public void processCase(int key, Label end) {
0785: e.getfield(getCallbackField(key));
0786: e.goTo(end);
0787: }
0788:
0789: public void processDefault() {
0790: e.pop(); // stack height
0791: e.aconst_null();
0792: }
0793: });
0794: e.return_value();
0795: e.end_method();
0796: }
0797:
0798: private void emitSetCallback(ClassEmitter ce, int[] keys) {
0799: final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0800: SET_CALLBACK, null);
0801: e.load_this ();
0802: e.load_arg(1);
0803: e.load_arg(0);
0804: e.process_switch(keys, new ProcessSwitchCallback() {
0805: public void processCase(int key, Label end) {
0806: e.checkcast(callbackTypes[key]);
0807: e.putfield(getCallbackField(key));
0808: e.goTo(end);
0809: }
0810:
0811: public void processDefault() {
0812: // TODO: error?
0813: e.pop2(); // stack height
0814: }
0815: });
0816: e.return_value();
0817: e.end_method();
0818: }
0819:
0820: private void emitSetCallbacks(ClassEmitter ce) {
0821: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0822: SET_CALLBACKS, null);
0823: e.load_this ();
0824: e.load_arg(0);
0825: for (int i = 0; i < callbackTypes.length; i++) {
0826: e.dup2();
0827: e.aaload(i);
0828: e.checkcast(callbackTypes[i]);
0829: e.putfield(getCallbackField(i));
0830: }
0831: e.return_value();
0832: e.end_method();
0833: }
0834:
0835: private void emitGetCallbacks(ClassEmitter ce) {
0836: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0837: GET_CALLBACKS, null);
0838: e.load_this ();
0839: e.invoke_static_this (BIND_CALLBACKS);
0840: e.load_this ();
0841: e.push(callbackTypes.length);
0842: e.newarray(CALLBACK);
0843: for (int i = 0; i < callbackTypes.length; i++) {
0844: e.dup();
0845: e.push(i);
0846: e.load_this ();
0847: e.getfield(getCallbackField(i));
0848: e.aastore();
0849: }
0850: e.return_value();
0851: e.end_method();
0852: }
0853:
0854: private void emitNewInstanceCallbacks(ClassEmitter ce) {
0855: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0856: NEW_INSTANCE, null);
0857: e.load_arg(0);
0858: e.invoke_static_this (SET_THREAD_CALLBACKS);
0859: emitCommonNewInstance(e);
0860: }
0861:
0862: private void emitCommonNewInstance(CodeEmitter e) {
0863: e.new_instance_this ();
0864: e.dup();
0865: e.invoke_constructor_this ();
0866: e.aconst_null();
0867: e.invoke_static_this (SET_THREAD_CALLBACKS);
0868: e.return_value();
0869: e.end_method();
0870: }
0871:
0872: private void emitNewInstanceCallback(ClassEmitter ce) {
0873: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0874: SINGLE_NEW_INSTANCE, null);
0875: switch (callbackTypes.length) {
0876: case 0:
0877: // TODO: make sure Callback is null
0878: break;
0879: case 1:
0880: // for now just make a new array; TODO: optimize
0881: e.push(1);
0882: e.newarray(CALLBACK);
0883: e.dup();
0884: e.push(0);
0885: e.load_arg(0);
0886: e.aastore();
0887: e.invoke_static_this (SET_THREAD_CALLBACKS);
0888: break;
0889: default:
0890: e.throw_exception(ILLEGAL_STATE_EXCEPTION,
0891: "More than one callback object required");
0892: }
0893: emitCommonNewInstance(e);
0894: }
0895:
0896: private void emitNewInstanceMultiarg(ClassEmitter ce,
0897: List constructors) {
0898: final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
0899: MULTIARG_NEW_INSTANCE, null);
0900: e.load_arg(2);
0901: e.invoke_static_this (SET_THREAD_CALLBACKS);
0902: e.new_instance_this ();
0903: e.dup();
0904: e.load_arg(0);
0905: EmitUtils.constructor_switch(e, constructors,
0906: new ObjectSwitchCallback() {
0907: public void processCase(Object key, Label end) {
0908: MethodInfo constructor = (MethodInfo) key;
0909: Type types[] = constructor.getSignature()
0910: .getArgumentTypes();
0911: for (int i = 0; i < types.length; i++) {
0912: e.load_arg(1);
0913: e.push(i);
0914: e.aaload();
0915: e.unbox(types[i]);
0916: }
0917: e.invoke_constructor_this (constructor
0918: .getSignature());
0919: e.goTo(end);
0920: }
0921:
0922: public void processDefault() {
0923: e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION,
0924: "Constructor not found");
0925: }
0926: });
0927: e.aconst_null();
0928: e.invoke_static_this (SET_THREAD_CALLBACKS);
0929: e.return_value();
0930: e.end_method();
0931: }
0932:
0933: private void emitMethods(final ClassEmitter ce, List methods,
0934: List actualMethods) {
0935: CallbackGenerator[] generators = CallbackInfo
0936: .getGenerators(callbackTypes);
0937:
0938: Map groups = new HashMap();
0939: final Map indexes = new HashMap();
0940: final Map originalModifiers = new HashMap();
0941: final Map positions = CollectionUtils.getIndexMap(methods);
0942:
0943: Iterator it1 = methods.iterator();
0944: Iterator it2 = (actualMethods != null) ? actualMethods
0945: .iterator() : null;
0946:
0947: while (it1.hasNext()) {
0948: MethodInfo method = (MethodInfo) it1.next();
0949: Method actualMethod = (it2 != null) ? (Method) it2.next()
0950: : null;
0951: int index = filter.accept(actualMethod);
0952: if (index >= callbackTypes.length) {
0953: throw new IllegalArgumentException(
0954: "Callback filter returned an index that is too large: "
0955: + index);
0956: }
0957: originalModifiers.put(method, new Integer(
0958: (actualMethod != null) ? actualMethod
0959: .getModifiers() : method.getModifiers()));
0960: indexes.put(method, new Integer(index));
0961: List group = (List) groups.get(generators[index]);
0962: if (group == null) {
0963: groups.put(generators[index], group = new ArrayList(
0964: methods.size()));
0965: }
0966: group.add(method);
0967: }
0968:
0969: Set seenGen = new HashSet();
0970: CodeEmitter se = ce.getStaticHook();
0971: se.new_instance(THREAD_LOCAL);
0972: se.dup();
0973: se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
0974: se.putfield(THREAD_CALLBACKS_FIELD);
0975:
0976: final Object[] state = new Object[1];
0977: CallbackGenerator.Context context = new CallbackGenerator.Context() {
0978: public ClassLoader getClassLoader() {
0979: return Enhancer.this .getClassLoader();
0980: }
0981:
0982: public int getOriginalModifiers(MethodInfo method) {
0983: return ((Integer) originalModifiers.get(method))
0984: .intValue();
0985: }
0986:
0987: public int getIndex(MethodInfo method) {
0988: return ((Integer) indexes.get(method)).intValue();
0989: }
0990:
0991: public void emitCallback(CodeEmitter e, int index) {
0992: emitCurrentCallback(e, index);
0993: }
0994:
0995: public Signature getImplSignature(MethodInfo method) {
0996: return rename(method.getSignature(),
0997: ((Integer) positions.get(method)).intValue());
0998: }
0999:
1000: public CodeEmitter beginMethod(ClassEmitter ce,
1001: MethodInfo method) {
1002: CodeEmitter e = EmitUtils.begin_method(ce, method);
1003: if (!interceptDuringConstruction
1004: && !TypeUtils.isAbstract(method.getModifiers())) {
1005: Label constructed = e.make_label();
1006: e.load_this ();
1007: e.getfield(CONSTRUCTED_FIELD);
1008: e.if_jump(e.NE, constructed);
1009: e.load_this ();
1010: e.load_args();
1011: e.super _invoke();
1012: e.return_value();
1013: e.mark(constructed);
1014: }
1015: return e;
1016: }
1017: };
1018: for (int i = 0; i < callbackTypes.length; i++) {
1019: CallbackGenerator gen = generators[i];
1020: if (!seenGen.contains(gen)) {
1021: seenGen.add(gen);
1022: final List fmethods = (List) groups.get(gen);
1023: if (fmethods != null) {
1024: try {
1025: gen.generate(ce, context, fmethods);
1026: gen.generateStatic(se, context, fmethods);
1027: } catch (RuntimeException x) {
1028: throw x;
1029: } catch (Exception x) {
1030: throw new CodeGenerationException(x);
1031: }
1032: }
1033: }
1034: }
1035: se.return_value();
1036: se.end_method();
1037: }
1038:
1039: private void emitSetThreadCallbacks(ClassEmitter ce) {
1040: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC
1041: | Constants.ACC_STATIC, SET_THREAD_CALLBACKS, null);
1042: e.getfield(THREAD_CALLBACKS_FIELD);
1043: e.load_arg(0);
1044: e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
1045: e.return_value();
1046: e.end_method();
1047: }
1048:
1049: private void emitSetStaticCallbacks(ClassEmitter ce) {
1050: CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC
1051: | Constants.ACC_STATIC, SET_STATIC_CALLBACKS, null);
1052: e.load_arg(0);
1053: e.putfield(STATIC_CALLBACKS_FIELD);
1054: e.return_value();
1055: e.end_method();
1056: }
1057:
1058: private void emitCurrentCallback(CodeEmitter e, int index) {
1059: e.load_this ();
1060: e.getfield(getCallbackField(index));
1061: e.dup();
1062: Label end = e.make_label();
1063: e.ifnonnull(end);
1064: e.pop(); // stack height
1065: e.load_this ();
1066: e.invoke_static_this (BIND_CALLBACKS);
1067: e.load_this ();
1068: e.getfield(getCallbackField(index));
1069: e.mark(end);
1070: }
1071:
1072: private void emitBindCallbacks(ClassEmitter ce) {
1073: CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
1074: BIND_CALLBACKS, null);
1075: Local me = e.make_local();
1076: e.load_arg(0);
1077: e.checkcast_this ();
1078: e.store_local(me);
1079:
1080: Label end = e.make_label();
1081: e.load_local(me);
1082: e.getfield(BOUND_FIELD);
1083: e.if_jump(e.NE, end);
1084: e.load_local(me);
1085: e.push(1);
1086: e.putfield(BOUND_FIELD);
1087:
1088: e.getfield(THREAD_CALLBACKS_FIELD);
1089: e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
1090: e.dup();
1091: Label found_callback = e.make_label();
1092: e.ifnonnull(found_callback);
1093: e.pop();
1094:
1095: e.getfield(STATIC_CALLBACKS_FIELD);
1096: e.dup();
1097: e.ifnonnull(found_callback);
1098: e.pop();
1099: e.goTo(end);
1100:
1101: e.mark(found_callback);
1102: e.checkcast(CALLBACK_ARRAY);
1103: e.load_local(me);
1104: e.swap();
1105: for (int i = callbackTypes.length - 1; i >= 0; i--) {
1106: if (i != 0) {
1107: e.dup2();
1108: }
1109: e.aaload(i);
1110: e.checkcast(callbackTypes[i]);
1111: e.putfield(getCallbackField(i));
1112: }
1113:
1114: e.mark(end);
1115: e.return_value();
1116: e.end_method();
1117: }
1118:
1119: private static String getCallbackField(int index) {
1120: return "CGLIB$CALLBACK_" + index;
1121: }
1122: }
|