KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > cglib > proxy > Enhancer


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2002 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  * if any, must include the following acknowledgment:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowledgment may appear in the software itself,
24  * if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  * not be used to endorse or promote products derived from this
28  * software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  * nor may "Apache" appear in their name, without prior written
33  * permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation. For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */

54 package org.logicalcobwebs.cglib.proxy;
55
56 import org.logicalcobwebs.cglib.core.*;
57 import java.lang.reflect.Method JavaDoc;
58 import java.util.*;
59 import org.logicalcobwebs.asm.ClassVisitor;
60
61 /**
62  * Generates dynamic subclasses to enable method interception. This
63  * class started as a substitute for the standard Dynamic Proxy support
64  * included with JDK 1.3, but one that allowed the proxies to extend a
65  * concrete base class, in addition to implementing interfaces. The dynamically
66  * generated subclasses override the non-final methods of the superclass and
67  * have hooks which callback to user-defined interceptor
68  * implementations.
69  * <p>
70  * The original and most general callback type is the {@link MethodInterceptor}, which
71  * in AOP terms enables "around advice"--that is, you can invoke custom code both before
72  * and after the invocation of the "super" method. In addition you can modify the
73  * arguments before calling the super method, or not call it at all.
74  * <p>
75  * Although <code>MethodInterceptor</code> is generic enough to meet any
76  * interception need, it is often overkill. For simplicity and performance, additional
77  * specialized callback types, such as {@link LazyLoader} are also available.
78  * Often a single callback will be used per enhanced class, but you can control
79  * which callback is used on a per-method basis with a {@link CallbackFilter}.
80  * <p>
81  * The most common uses of this class are embodied in the static helper methods. For
82  * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
83  * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
84  * <p>
85  * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
86  * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
87  * to change the callbacks of an existing object, as well as a faster and easier way to create
88  * new instances of the same type.
89  * <p>
90  * For an almost drop-in replacement for
91  * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
92  */

93 public class Enhancer extends AbstractClassGenerator
94 {
95     private static final Source SOURCE = new Source(Enhancer.class.getName());
96     private static final EnhancerKey KEY_FACTORY =
97       (EnhancerKey)KeyFactory.create(EnhancerKey.class, KeyFactory.CLASS_BY_NAME);
98
99     interface EnhancerKey {
100         public Object JavaDoc newInstance(Class JavaDoc type,
101                                   Class JavaDoc[] interfaces,
102                                   CallbackFilter filter,
103                                   Class JavaDoc[] callbackTypes,
104                                   boolean useFactory);
105     }
106
107     private Class JavaDoc[] interfaces;
108     private CallbackFilter filter;
109     private Callback[] callbacks;
110     private Class JavaDoc[] callbackTypes;
111     private boolean classOnly;
112     private Class JavaDoc superclass;
113     private Class JavaDoc[] argumentTypes;
114     private Object JavaDoc[] arguments;
115     private boolean useFactory = true;
116
117     /**
118      * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
119      * object should be used for each generated object, and should not
120      * be shared across threads. To create additional instances of a
121      * generated class, use the <code>Factory</code> interface.
122      * @see Factory
123      */

124     public Enhancer() {
125         super(SOURCE);
126     }
127
128     /**
129      * Set the class which the generated class will extend. As a convenience,
130      * if the supplied superclass is actually an interface, <code>setInterfaces</code>
131      * will be called with the appropriate argument instead.
132      * A non-interface argument must not be declared as final, and must have an
133      * accessible constructor.
134      * @param superclass class to extend or interface to implement
135      * @see #setInterfaces(Class[])
136      */

137     public void setSuperclass(Class JavaDoc superclass) {
138         if (superclass != null && superclass.isInterface()) {
139             setInterfaces(new Class JavaDoc[]{ superclass });
140         } else if (superclass != null && superclass.equals(Object JavaDoc.class)) {
141             // affects choice of ClassLoader
142
this.superclass = null;
143         } else {
144             this.superclass = superclass;
145         }
146     }
147
148     /**
149      * Set the interfaces to implement. The <code>Factory</code> interface will
150      * always be implemented regardless of what is specified here.
151      * @param interfaces array of interfaces to implement, or null
152      * @see Factory
153      */

154     public void setInterfaces(Class JavaDoc[] interfaces) {
155         this.interfaces = interfaces;
156     }
157
158     /**
159      * Set the {@link CallbackFilter} used to map the generated class' methods
160      * to a particular callback index.
161      * New object instances will always use the same mapping, but may use different
162      * actual callback objects.
163      * @param filter the callback filter to use when generating a new class
164      * @see #setCallbacks
165      */

166     public void setCallbackFilter(CallbackFilter filter) {
167         this.filter = filter;
168     }
169
170     /**
171      * Set the single {@link Callback} to use.
172      * Ignored if you use {@link #createClass}.
173      * @param callback the callback to use for all methods
174      * @see #setCallbacks
175      */

176     public void setCallback(final Callback callback) {
177         setCallbacks(new Callback[]{ callback });
178     }
179
180     /**
181      * Set the array of callbacks to use.
182      * Ignored if you use {@link #createClass}.
183      * You must use a {@link CallbackFilter} to specify the index into this
184      * array for each method in the proxied class.
185      * @param callbacks the callback array
186      * @see #setCallbackFilter
187      * @see #setCallback
188      */

189     public void setCallbacks(Callback[] callbacks) {
190         if (callbacks != null && callbacks.length == 0) {
191             throw new IllegalArgumentException JavaDoc("Array cannot be empty");
192         }
193         this.callbacks = callbacks;
194     }
195
196     /**
197      * Set whether the enhanced object instances should implement
198      * the {@link Factory} interface.
199      * This was added for tools that need for proxies to be more
200      * indistinguishable from their targets. Also, in some cases it may
201      * be necessary to disable the <code>Factory</code> interface to
202      * prevent code from changing the underlying callbacks.
203      * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
204      */

205     public void setUseFactory(boolean useFactory) {
206         this.useFactory = useFactory;
207     }
208
209     /**
210      * Set the single type of {@link Callback} to use.
211      * This may be used instead of {@link #setCallback} when calling
212      * {@link #createClass}, since it may not be possible to have
213      * an array of actual callback instances.
214      * @param callbackType the type of callback to use for all methods
215      * @see #setCallbackTypes
216      */

217     public void setCallbackType(Class JavaDoc callbackType) {
218         setCallbackTypes(new Class JavaDoc[]{ callbackType });
219     }
220
221     /**
222      * Set the array of callback types to use.
223      * This may be used instead of {@link #setCallbacks} when calling
224      * {@link #createClass}, since it may not be possible to have
225      * an array of actual callback instances.
226      * You must use a {@link CallbackFilter} to specify the index into this
227      * array for each method in the proxied class.
228      * @param callbackTypes the array of callback types
229      */

230     public void setCallbackTypes(Class JavaDoc[] callbackTypes) {
231         if (callbackTypes != null && callbackTypes.length == 0) {
232             throw new IllegalArgumentException JavaDoc("Array cannot be empty");
233         }
234         this.callbackTypes = callbackTypes;
235     }
236
237     /**
238      * Generate a new class if necessary and uses the specified
239      * callbacks (if any) to create a new object instance.
240      * Uses the no-arg constructor of the superclass.
241      * @return a new instance
242      */

243     public Object JavaDoc create() {
244         classOnly = false;
245         argumentTypes = null;
246         return createHelper();
247     }
248
249     /**
250      * Generate a new class if necessary and uses the specified
251      * callbacks (if any) to create a new object instance.
252      * Uses the constructor of the superclass matching the <code>argumentTypes</code>
253      * parameter, with the given arguments.
254      * @param argumentTypes constructor signature
255      * @param arguments compatible wrapped arguments to pass to constructor
256      * @return a new instance
257      */

258     public Object JavaDoc create(Class JavaDoc[] argumentTypes, Object JavaDoc[] arguments) {
259         classOnly = false;
260         if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
261             throw new IllegalArgumentException JavaDoc("Arguments must be non-null and of equal length");
262         }
263         this.argumentTypes = argumentTypes;
264         this.arguments = arguments;
265         return createHelper();
266     }
267
268     /**
269      * Generate a new class if necessary and return it without creating a new instance.
270      * This ignores any callbacks that have been set.
271      * To create a new instance you will have to use reflection, and methods
272      * called during the constructor will not be intercepted. To avoid this problem,
273      * use the multi-arg <code>create</code> method.
274      * @see #create(Class[], Object[])
275      */

276     public Class JavaDoc createClass() {
277         classOnly = true;
278         return (Class JavaDoc)createHelper();
279     }
280
281     private Object JavaDoc createHelper() {
282         if (classOnly ^ (callbacks == null)) {
283             if (classOnly) {
284                 throw new IllegalStateException JavaDoc("createClass does not accept callbacks");
285             } else {
286                 throw new IllegalStateException JavaDoc("callbacks are required unless using createClass");
287             }
288         }
289         if (callbacks == null && callbackTypes == null) {
290             throw new IllegalStateException JavaDoc("Either callbacks or callback types are always required");
291         }
292         if (callbacks != null && callbackTypes != null) {
293             if (callbacks.length != callbackTypes.length) {
294                 throw new IllegalStateException JavaDoc("Lengths of callback and callback types array must be the same");
295             }
296             for (int i = 0; i < callbacks.length; i++) {
297                 // make sure all classes are callbacks
298
CallbackUtils.getGenerator(callbackTypes[i]);
299                 if (callbacks[i] == null) {
300                     throw new IllegalStateException JavaDoc("Callback cannot be null");
301                 }
302                 if (!callbackTypes[i].isAssignableFrom(callbacks[i].getClass())) {
303                     throw new IllegalStateException JavaDoc("Callback " + callbacks[i] + " is not assignable to " + callbackTypes[i]);
304                 }
305             }
306         } else if (callbacks != null) {
307             callbackTypes = new Class JavaDoc[callbacks.length];
308             for (int i = 0; i < callbacks.length; i++) {
309                 callbackTypes[i] = CallbackUtils.determineType(callbacks[i]);
310             }
311         } else {
312             for (int i = 0; i < callbackTypes.length; i++) {
313                 // make sure all classes are callbacks
314
CallbackUtils.getGenerator(callbackTypes[i]);
315             }
316         }
317         if (filter == null) {
318             if (callbackTypes.length > 1) {
319                 throw new IllegalStateException JavaDoc("Multiple callback types possible but no filter specified");
320             }
321             filter = CallbackFilter.ALL_ZERO;
322         }
323         
324         if (superclass != null) {
325             setNamePrefix(superclass.getName());
326         } else if (interfaces != null) {
327             setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
328         }
329         Object JavaDoc key = KEY_FACTORY.newInstance(superclass, interfaces, filter, callbackTypes, useFactory);
330         return super.create(key);
331     }
332
333     protected ClassLoader JavaDoc getDefaultClassLoader() {
334         if (superclass != null) {
335             return superclass.getClassLoader();
336         } else if (interfaces != null) {
337             return interfaces[0].getClassLoader();
338         } else {
339             return null;
340         }
341     }
342
343     public void generateClass(ClassVisitor v) throws Exception JavaDoc {
344         new EnhancerEmitter(v, getClassName(), superclass, interfaces, filter, callbackTypes, useFactory);
345     }
346
347     protected Object JavaDoc firstInstance(Class JavaDoc type) throws Exception JavaDoc {
348         if (classOnly) {
349             return type;
350         } else {
351             return createUsingReflection(type);
352         }
353     }
354
355     protected Object JavaDoc nextInstance(Object JavaDoc instance) {
356         Class JavaDoc protoclass = (instance instanceof Class JavaDoc) ? (Class JavaDoc)instance : instance.getClass();
357         if (classOnly) {
358             return protoclass;
359         } else if (instance instanceof Factory) {
360             if (argumentTypes != null) {
361                 return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
362             } else {
363                 return ((Factory)instance).newInstance(callbacks);
364             }
365         } else {
366             return createUsingReflection(protoclass);
367         }
368     }
369
370     private Object JavaDoc createUsingReflection(Class JavaDoc type) {
371         EnhancerEmitter.setThreadCallbacks(type, callbacks);
372         if (argumentTypes != null) {
373             return ReflectUtils.newInstance(type, argumentTypes, arguments);
374         } else {
375             return ReflectUtils.newInstance(type);
376         }
377     }
378
379     /**
380      * Helper method to create an intercepted object.
381      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
382      * instead of this static method.
383      * @param type class to extend or interface to implement
384      * @param callback the callback to use for all methods
385      */

386     public static Object JavaDoc create(Class JavaDoc type, Callback callback) {
387         Enhancer e = new Enhancer();
388         e.setSuperclass(type);
389         e.setCallback(callback);
390         return e.create();
391     }
392
393     /**
394      * Helper method to create an intercepted object.
395      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
396      * instead of this static method.
397      * @param type class to extend or interface to implement
398      * @param interfaces array of interfaces to implement, or null
399      * @param callback the callback to use for all methods
400      */

401     public static Object JavaDoc create(Class JavaDoc superclass, Class JavaDoc interfaces[], Callback callback) {
402         Enhancer e = new Enhancer();
403         e.setSuperclass(superclass);
404         e.setInterfaces(interfaces);
405         e.setCallback(callback);
406         return e.create();
407     }
408
409     /**
410      * Helper method to create an intercepted object.
411      * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
412      * instead of this static method.
413      * @param type class to extend or interface to implement
414      * @param interfaces array of interfaces to implement, or null
415      * @param filter the callback filter to use when generating a new class
416      * @param callbacks callback implementations to use for the enhanced object
417      */

418     public static Object JavaDoc create(Class JavaDoc superclass, Class JavaDoc[] interfaces, CallbackFilter filter, Callback[] callbacks) {
419         Enhancer e = new Enhancer();
420         e.setSuperclass(superclass);
421         e.setInterfaces(interfaces);
422         e.setCallbackFilter(filter);
423         e.setCallbacks(callbacks);
424         return e.create();
425     }
426 }
427
Popular Tags