KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > cglib > proxy > Enhancer


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 JavaDoc;
19 import java.lang.reflect.InvocationTargetException JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
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 JavaDoc 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 JavaDoc BOUND_FIELD = "CGLIB$BOUND";
73     private static final String JavaDoc THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
74     private static final String JavaDoc STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
75     private static final String JavaDoc SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
76     private static final String JavaDoc SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
77     private static final String JavaDoc 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 JavaDoc newInstance(String JavaDoc type,
125                                   String JavaDoc[] interfaces,
126                                   CallbackFilter filter,
127                                   Type[] callbackTypes,
128                                   boolean useFactory,
129                                   boolean interceptDuringConstruction,
130                                   Long JavaDoc serialVersionUID);
131     }
132
133     private Class JavaDoc[] interfaces;
134     private CallbackFilter filter;
135     private Callback[] callbacks;
136     private Type[] callbackTypes;
137     private boolean classOnly;
138     private Class JavaDoc superclass;
139     private Class JavaDoc[] argumentTypes;
140     private Object JavaDoc[] arguments;
141     private boolean useFactory = true;
142     private Long JavaDoc 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 JavaDoc superclass) {
166         if (superclass != null && superclass.isInterface()) {
167             setInterfaces(new Class JavaDoc[]{ superclass });
168         } else if (superclass != null && superclass.equals(Object JavaDoc.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 JavaDoc[] 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 JavaDoc("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 JavaDoc callbackType) {
257         setCallbackTypes(new Class JavaDoc[]{ 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 JavaDoc[] callbackTypes) {
270         if (callbackTypes != null && callbackTypes.length == 0) {
271             throw new IllegalArgumentException JavaDoc("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 JavaDoc 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 JavaDoc create(Class JavaDoc[] argumentTypes, Object JavaDoc[] arguments) {
298         classOnly = false;
299         if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
300             throw new IllegalArgumentException JavaDoc("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 JavaDoc createClass() {
316         classOnly = true;
317         return (Class JavaDoc)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 JavaDoc sUID) {
325         serialVersionUID = sUID;
326     }
327
328     private void validate() {
329         if (classOnly ^ (callbacks == null)) {
330             if (classOnly) {
331                 throw new IllegalStateException JavaDoc("createClass does not accept callbacks");
332             } else {
333                 throw new IllegalStateException JavaDoc("Callbacks are required");
334             }
335         }
336         if (classOnly && (callbackTypes == null)) {
337             throw new IllegalStateException JavaDoc("Callback types are required");
338         }
339         if (callbacks != null && callbackTypes != null) {
340             if (callbacks.length != callbackTypes.length) {
341                 throw new IllegalStateException JavaDoc("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 JavaDoc("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 JavaDoc("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 JavaDoc("Interfaces cannot be null");
362                 }
363                 if (!interfaces[i].isInterface()) {
364                     throw new IllegalStateException JavaDoc(interfaces[i] + " is not an interface");
365                 }
366             }
367         }
368     }
369
370     private Object JavaDoc 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 JavaDoc 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 JavaDoc superclass, Class JavaDoc[] interfaces, List methods)
415     {
416         getMethods(superclass, interfaces, methods, null, null);
417     }
418
419     private static void getMethods(Class JavaDoc superclass, Class JavaDoc[] 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 JavaDoc {
443         Class JavaDoc sc = (superclass == null) ? Object JavaDoc.class : superclass;
444
445         if (TypeUtils.isFinal(sc.getModifiers()))
446             throw new IllegalArgumentException JavaDoc("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 JavaDoc transform(Object JavaDoc value) {
460                 Method JavaDoc method = (Method JavaDoc)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 JavaDoc sc, List constructors) {
529         CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
530         if (constructors.size() == 0)
531             throw new IllegalArgumentException JavaDoc("No visible constructors in " + sc);
532     }
533
534     protected Object JavaDoc firstInstance(Class JavaDoc type) throws Exception JavaDoc {
535         if (classOnly) {
536             return type;
537         } else {
538             return createUsingReflection(type);
539         }
540     }
541
542     protected Object JavaDoc nextInstance(Object JavaDoc instance) {
543         Class JavaDoc protoclass = (instance instanceof Class JavaDoc) ? (Class JavaDoc)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}).
572      * @param generatedClass a class previously created by {@link Enhancer}
573      * @param callbacks the array of callbacks to use when instances of the generated
574      * class are created
575      * @see #setUseFactory
576      */

577     public static void registerCallbacks(Class JavaDoc generatedClass, Callback[] callbacks) {
578         setThreadCallbacks(generatedClass, callbacks);
579     }
580
581     /**
582      * Similar to {@link #registerCallbacks}, but suitable for use
583      * when multiple threads will be creating instances of the generated class.
584      * The thread-level callbacks will always override the static callbacks.
585      * Static callbacks are never cleared.
586      * @param generatedClass a class previously created by {@link Enhancer}
587      * @param callbacks the array of callbacks to use when instances of the generated
588      * class are created
589      */

590     public static void registerStaticCallbacks(Class JavaDoc generatedClass, Callback[] callbacks) {
591         setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
592     }
593
594     /**
595      * Determine if a class was generated using <code>Enhancer</code>.
596      * @param type any class
597      * @return whether the class was generated using <code>Enhancer</code>
598      */

599     public static boolean isEnhanced(Class JavaDoc type) {
600         try {
601             getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
602             return true;
603         } catch (NoSuchMethodException JavaDoc e) {
604             return false;
605         }
606     }
607
608     private static void setThreadCallbacks(Class JavaDoc type, Callback[] callbacks) {
609         setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
610     }
611
612     private static void setCallbacksHelper(Class JavaDoc type, Callback[] callbacks, String JavaDoc methodName) {
613         // TODO: optimize
614
try {
615             Method JavaDoc setter = getCallbacksSetter(type, methodName);
616             setter.invoke(null, new Object JavaDoc[]{ callbacks });
617         } catch (NoSuchMethodException JavaDoc e) {
618             throw new IllegalArgumentException JavaDoc(type + " is not an enhanced class");
619         } catch (IllegalAccessException JavaDoc e) {
620             throw new CodeGenerationException(e);
621         } catch (InvocationTargetException JavaDoc e) {
622             throw new CodeGenerationException(e);
623         }
624     }
625
626     private static Method JavaDoc getCallbacksSetter(Class JavaDoc type, String JavaDoc methodName) throws NoSuchMethodException JavaDoc {
627         return type.getDeclaredMethod(methodName, new Class JavaDoc[]{ Callback[].class });
628     }
629
630     private Object JavaDoc createUsingReflection(Class JavaDoc type) {
631         setThreadCallbacks(type, callbacks);
632         try{
633         
634         if (argumentTypes != null) {
635             
636              return ReflectUtils.newInstance(type, argumentTypes, arguments);
637              
638         } else {
639             
640             return ReflectUtils.newInstance(type);
641             
642         }
643         }finally{
644          // clear thread callbacks to allow them to be gc'd
645
setThreadCallbacks(type, null);
646         }
647     }
648
649     /**
650      * Helper method to create an intercepted object.
651      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
652      * instead of this static method.
653      * @param type class to extend or interface to implement
654      * @param callback the callback to use for all methods
655      */

656     public static Object JavaDoc create(Class JavaDoc type, Callback callback) {
657         Enhancer e = new Enhancer();
658         e.setSuperclass(type);
659         e.setCallback(callback);
660         return e.create();
661     }
662
663     /**
664      * Helper method to create an intercepted object.
665      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
666      * instead of this static method.
667      * @param type class to extend or interface to implement
668      * @param interfaces array of interfaces to implement, or null
669      * @param callback the callback to use for all methods
670      */

671     public static Object JavaDoc create(Class JavaDoc superclass, Class JavaDoc interfaces[], Callback callback) {
672         Enhancer e = new Enhancer();
673         e.setSuperclass(superclass);
674         e.setInterfaces(interfaces);
675         e.setCallback(callback);
676         return e.create();
677     }
678
679     /**
680      * Helper method to create an intercepted object.
681      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
682      * instead of this static method.
683      * @param type class to extend or interface to implement
684      * @param interfaces array of interfaces to implement, or null
685      * @param filter the callback filter to use when generating a new class
686      * @param callbacks callback implementations to use for the enhanced object
687      */

688     public static Object JavaDoc create(Class JavaDoc superclass, Class JavaDoc[] interfaces, CallbackFilter filter, Callback[] callbacks) {
689         Enhancer e = new Enhancer();
690         e.setSuperclass(superclass);
691         e.setInterfaces(interfaces);
692         e.setCallbackFilter(filter);
693         e.setCallbacks(callbacks);
694         return e.create();
695     }
696
697     private void emitConstructors(ClassEmitter ce, List constructors) {
698         boolean seenNull = false;
699         for (Iterator it = constructors.iterator(); it.hasNext();) {
700             MethodInfo constructor = (MethodInfo)it.next();
701             CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC);
702             e.load_this();
703             e.dup();
704             e.load_args();
705             Signature sig = constructor.getSignature();
706             seenNull = seenNull || sig.getDescriptor().equals("()V");
707             e.super_invoke_constructor(sig);
708             e.invoke_static_this(BIND_CALLBACKS);
709             if (!interceptDuringConstruction) {
710                 e.load_this();
711                 e.push(1);
712                 e.putfield(CONSTRUCTED_FIELD);
713             }
714             e.return_value();
715             e.end_method();
716         }
717         if (!classOnly && !seenNull && arguments == null)
718             throw new IllegalArgumentException JavaDoc("Superclass has no null constructors but no arguments were given");
719     }
720
721     private int[] getCallbackKeys() {
722         int[] keys = new int[callbackTypes.length];
723         for (int i = 0; i < callbackTypes.length; i++) {
724             keys[i] = i;
725         }
726         return keys;
727     }
728
729     private void emitGetCallback(ClassEmitter ce, int[] keys) {
730         final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null);
731         e.load_this();
732         e.invoke_static_this(BIND_CALLBACKS);
733         e.load_this();
734         e.load_arg(0);
735         e.process_switch(keys, new ProcessSwitchCallback() {
736             public void processCase(int key, Label end) {
737                 e.getfield(getCallbackField(key));
738                 e.goTo(end);
739             }
740             public void processDefault() {
741                 e.pop(); // stack height
742
e.aconst_null();
743             }
744         });
745         e.return_value();
746         e.end_method();
747     }
748
749     private void emitSetCallback(ClassEmitter ce, int[] keys) {
750         final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null);
751         e.load_arg(0);
752         e.process_switch(keys, new ProcessSwitchCallback() {
753             public void processCase(int key, Label end) {
754                 e.load_this();
755                 e.load_arg(1);
756                 e.checkcast(callbackTypes[key]);
757                 e.putfield(getCallbackField(key));
758                 e.goTo(end);
759             }
760             public void processDefault() {
761                 // TODO: error?
762
}
763         });
764         e.return_value();
765         e.end_method();
766     }
767
768     private void emitSetCallbacks(ClassEmitter ce) {
769         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null);
770         e.load_this();
771         e.load_arg(0);
772         for (int i = 0; i < callbackTypes.length; i++) {
773             e.dup2();
774             e.aaload(i);
775             e.checkcast(callbackTypes[i]);
776             e.putfield(getCallbackField(i));
777         }
778         e.return_value();
779         e.end_method();
780     }
781
782     private void emitGetCallbacks(ClassEmitter ce) {
783         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null);
784         e.load_this();
785         e.invoke_static_this(BIND_CALLBACKS);
786         e.load_this();
787         e.push(callbackTypes.length);
788         e.newarray(CALLBACK);
789         for (int i = 0; i < callbackTypes.length; i++) {
790             e.dup();
791             e.push(i);
792             e.load_this();
793             e.getfield(getCallbackField(i));
794             e.aastore();
795         }
796         e.return_value();
797         e.end_method();
798     }
799
800     private void emitNewInstanceCallbacks(ClassEmitter ce) {
801         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
802         e.load_arg(0);
803         e.invoke_static_this(SET_THREAD_CALLBACKS);
804         emitCommonNewInstance(e);
805     }
806
807     private void emitCommonNewInstance(CodeEmitter e) {
808         e.new_instance_this();
809         e.dup();
810         e.invoke_constructor_this();
811         e.aconst_null();
812         e.invoke_static_this(SET_THREAD_CALLBACKS);
813         e.return_value();
814         e.end_method();
815     }
816     
817     private void emitNewInstanceCallback(ClassEmitter ce) {
818         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null);
819         switch (callbackTypes.length) {
820         case 0:
821             // TODO: make sure Callback is null
822
break;
823         case 1:
824             // for now just make a new array; TODO: optimize
825
e.push(1);
826             e.newarray(CALLBACK);
827             e.dup();
828             e.push(0);
829             e.load_arg(0);
830             e.aastore();
831             e.invoke_static_this(SET_THREAD_CALLBACKS);
832             break;
833         default:
834             e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
835         }
836         emitCommonNewInstance(e);
837     }
838
839     private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) {
840         final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null);
841         e.load_arg(2);
842         e.invoke_static_this(SET_THREAD_CALLBACKS);
843         e.new_instance_this();
844         e.dup();
845         e.load_arg(0);
846         EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
847             public void processCase(Object JavaDoc key, Label end) {
848                 MethodInfo constructor = (MethodInfo)key;
849                 Type types[] = constructor.getSignature().getArgumentTypes();
850                 for (int i = 0; i < types.length; i++) {
851                     e.load_arg(1);
852                     e.push(i);
853                     e.aaload();
854                     e.unbox(types[i]);
855                 }
856                 e.invoke_constructor_this(constructor.getSignature());
857                 e.goTo(end);
858             }
859             public void processDefault() {
860                 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
861             }
862         });
863         e.aconst_null();
864         e.invoke_static_this(SET_THREAD_CALLBACKS);
865         e.return_value();
866         e.end_method();
867     }
868
869     private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) {
870         CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes);
871
872         Map groups = new HashMap();
873         final Map indexes = new HashMap();
874         final Map originalModifiers = new HashMap();
875         final Map positions = CollectionUtils.getIndexMap(methods);
876
877         Iterator it1 = methods.iterator();
878         Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null;
879
880         while (it1.hasNext()) {
881             MethodInfo method = (MethodInfo)it1.next();
882             Method JavaDoc actualMethod = (it2 != null) ? (Method JavaDoc)it2.next() : null;
883             int index = filter.accept(actualMethod);
884             if (index >= callbackTypes.length) {
885                 throw new IllegalArgumentException JavaDoc("Callback filter returned an index that is too large: " + index);
886             }
887             originalModifiers.put(method, new Integer JavaDoc((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()));
888             indexes.put(method, new Integer JavaDoc(index));
889             List group = (List)groups.get(generators[index]);
890             if (group == null) {
891                 groups.put(generators[index], group = new ArrayList(methods.size()));
892             }
893             group.add(method);
894         }
895
896         Set seenGen = new HashSet();
897         CodeEmitter se = ce.getStaticHook();
898         se.new_instance(THREAD_LOCAL);
899         se.dup();
900         se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
901         se.putfield(THREAD_CALLBACKS_FIELD);
902
903         final Object JavaDoc[] state = new Object JavaDoc[1];
904         CallbackGenerator.Context context = new CallbackGenerator.Context() {
905             public ClassLoader JavaDoc getClassLoader() {
906                 return Enhancer.this.getClassLoader();
907             }
908             public int getOriginalModifiers(MethodInfo method) {
909                 return ((Integer JavaDoc)originalModifiers.get(method)).intValue();
910             }
911             public int getIndex(MethodInfo method) {
912                 return ((Integer JavaDoc)indexes.get(method)).intValue();
913             }
914             public void emitCallback(CodeEmitter e, int index) {
915                 emitCurrentCallback(e, index);
916             }
917             public Signature getImplSignature(MethodInfo method) {
918                 return rename(method.getSignature(), ((Integer JavaDoc)positions.get(method)).intValue());
919             }
920             public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) {
921                 CodeEmitter e = EmitUtils.begin_method(ce, method);
922                 if (!interceptDuringConstruction &&
923                     !TypeUtils.isAbstract(method.getModifiers())) {
924                     Label constructed = e.make_label();
925                     e.load_this();
926                     e.getfield(CONSTRUCTED_FIELD);
927                     e.if_jump(e.NE, constructed);
928                     e.load_this();
929                     e.load_args();
930                     e.super_invoke();
931                     e.return_value();
932                     e.mark(constructed);
933                 }
934                 return e;
935             }
936         };
937         for (int i = 0; i < callbackTypes.length; i++) {
938             CallbackGenerator gen = generators[i];
939             if (!seenGen.contains(gen)) {
940                 seenGen.add(gen);
941                 final List fmethods = (List)groups.get(gen);
942                 if (fmethods != null) {
943                     try {
944                         gen.generate(ce, context, fmethods);
945                         gen.generateStatic(se, context, fmethods);
946                     } catch (RuntimeException JavaDoc x) {
947                         throw x;
948                     } catch (Exception JavaDoc x) {
949                         throw new CodeGenerationException(x);
950                     }
951                 }
952             }
953         }
954         se.return_value();
955         se.end_method();
956     }
957
958     private void emitSetThreadCallbacks(ClassEmitter ce) {
959         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
960                                         SET_THREAD_CALLBACKS,
961                                         null);
962         e.getfield(THREAD_CALLBACKS_FIELD);
963         e.load_arg(0);
964         e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
965         e.return_value();
966         e.end_method();
967     }
968
969     private void emitSetStaticCallbacks(ClassEmitter ce) {
970         CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
971                                         SET_STATIC_CALLBACKS,
972                                         null);
973         e.load_arg(0);
974         e.putfield(STATIC_CALLBACKS_FIELD);
975         e.return_value();
976         e.end_method();
977     }
978     
979     private void emitCurrentCallback(CodeEmitter e, int index) {
980         e.load_this();
981         e.getfield(getCallbackField(index));
982         e.dup();
983         Label end = e.make_label();
984         e.ifnonnull(end);
985         e.pop(); // stack height
986
e.load_this();
987         e.invoke_static_this(BIND_CALLBACKS);
988         e.load_this();
989         e.getfield(getCallbackField(index));
990         e.mark(end);
991     }
992
993     private void emitBindCallbacks(ClassEmitter ce) {
994         CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
995                                         BIND_CALLBACKS,
996                                         null);
997         Local me = e.make_local();
998         e.load_arg(0);
999         e.checkcast_this();
1000        e.store_local(me);
1001
1002        Label end = e.make_label();
1003        e.load_local(me);
1004        e.getfield(BOUND_FIELD);
1005        e.if_jump(e.NE, end);
1006        e.load_local(me);
1007        e.push(1);
1008        e.putfield(BOUND_FIELD);
1009
1010        e.getfield(THREAD_CALLBACKS_FIELD);
1011        e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
1012        e.dup();
1013        Label found_callback = e.make_label();
1014        e.ifnonnull(found_callback);
1015        e.pop();
1016
1017        e.getfield(STATIC_CALLBACKS_FIELD);
1018        e.dup();
1019        e.ifnonnull(found_callback);
1020        e.pop();
1021        e.goTo(end);
1022
1023        e.mark(found_callback);
1024        e.checkcast(CALLBACK_ARRAY);
1025        e.load_local(me);
1026        e.swap();
1027        for (int i = callbackTypes.length - 1; i >= 0; i--) {
1028            if (i != 0) {
1029                e.dup2();
1030            }
1031            e.aaload(i);
1032            e.checkcast(callbackTypes[i]);
1033            e.putfield(getCallbackField(i));
1034        }
1035
1036        e.mark(end);
1037        e.return_value();
1038        e.end_method();
1039    }
1040
1041    private static String JavaDoc getCallbackField(int index) {
1042        return "CGLIB$CALLBACK_" + index;
1043    }
1044}
1045
Popular Tags