KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > dynaop > Aspects


1 package dynaop;
2
3 import java.io.Serializable JavaDoc;
4 import java.util.ArrayList JavaDoc;
5 import java.util.Arrays JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.List JavaDoc;
8 import java.util.Properties JavaDoc;
9
10 import net.sf.cglib.Factory;
11
12 import dynaop.ClassPointcut;
13 import dynaop.ConfigurationException;
14 import dynaop.MethodPointcut;
15 import dynaop.ProxyType;
16 import dynaop.ProxyTypeBuilder;
17 import dynaop.util.Classes;
18 import dynaop.util.Closure;
19 import dynaop.util.NestedException;
20
21 /**
22  * Collection of aspects. Aspects map advice to pointcuts.
23  *
24  * <p>Treats <code>Object.class</code> as a special case interface allowing
25  * mixins or method interceptors to implement <code>hashCode()</code>,
26  * <code>equals(Object)</code>, and <code>toString()</code>.</p>
27  *
28  * @author Bob Lee (crazybob@crazybob.org)
29  */

30 public class Aspects {
31
32     static String JavaDoc DEFAULT_ASPECTS = "dynaop.bsh.BshAspects";
33     
34     // these interfaces will not be automatically added to the proxy.
35
static List JavaDoc IGNORED_INTERFACES = Arrays.asList(
36         new Class JavaDoc[] { Serializable JavaDoc.class, Proxy.class, ProxyAware.class,
37             Factory.class, Cloneable JavaDoc.class });
38     
39     List JavaDoc interfaceAspects = new ArrayList JavaDoc();
40     List JavaDoc interceptionAspects = new ArrayList JavaDoc();;
41     List JavaDoc mixinAspects = new ArrayList JavaDoc();
42
43     public Aspects() {}
44
45     Aspects(Aspects aspects) {
46         this.interfaceAspects = new ArrayList JavaDoc(aspects.interfaceAspects);
47         this.mixinAspects = new ArrayList JavaDoc(aspects.mixinAspects);
48         this.interceptionAspects = new ArrayList JavaDoc(aspects.interceptionAspects);
49     }
50     
51     void execute(List JavaDoc aspects, ProxyTypeBuilder builder) {
52         for (Iterator JavaDoc iterator = aspects.iterator(); iterator.hasNext();)
53             ((Closure) iterator.next()).execute(builder);
54     }
55     
56     /**
57      * Creates proxy type for target class. Returns <code>null</code> if no
58      * mixins or interceptors are present.
59      */

60     ProxyType createProxyType(Class JavaDoc targetClass,
61             ProxyTypeBuilder builder) {
62         execute(this.interfaceAspects, builder);
63         execute(this.interceptionAspects, builder);
64         execute(this.mixinAspects, builder);
65         
66         return (builder.isEmpty()) ? null : builder.createProxyType();
67     }
68
69     //********************************************************************
70
// interface methods.
71
//********************************************************************
72

73     /**
74      * Adds interfaces to classes picked by pointcut.
75      *
76      * @param classPointcut Classes to add interfaces to.
77      * @param interfaces Interfaces to add.
78      */

79     public void interfaces(ClassPointcut classPointcut,
80             final Class JavaDoc[] interfaces)
81             throws ConfigurationException {
82         if (interfaces == null)
83             throw new ConfigurationException("Interface array is null.");
84
85         Closure interfaceAspect = new Closure() {
86             public void execute(Object JavaDoc o) {
87                 ((ProxyTypeBuilder) o).addInterfaces(interfaces);
88             }
89         };
90         
91         interfaceAspects.add(
92             new ClassPointcutAspect(classPointcut, interfaceAspect));
93     }
94     
95     //********************************************************************
96
// mixin methods.
97
//********************************************************************
98

99     /**
100      * Adds mixin instance to classes picked by pointcut.
101      * Supports {@link ProxyAware}. Creates mixin instances using default
102      * constructor. Executes <code>initializer</code> against mixin instance
103      * after creation and before usage.
104      *
105      * @param classPointcut Classes to add mixin to.
106      * @param interfaces Interfaces mixin implements.
107      * @param mixinClass Mixin implementation.
108      * @param initializer Code that initializes mixin instance. Can be
109      * <code>null</code>.
110      */

111     public void mixin(ClassPointcut classPointcut,
112             final Class JavaDoc[] interfaces, Class JavaDoc mixinClass, Closure initializer)
113             throws ConfigurationException {
114         if (!Classes.implementsInterfaces(mixinClass, interfaces)) {
115             throw new ConfigurationException(mixinClass +
116                 " must implement each of " + Arrays.asList(interfaces) + ".");
117         }
118         
119         interfaces(classPointcut, interfaces);
120         
121         final InterceptorFactory factory =
122             new MixinInterceptorFactory(mixinClass, initializer);
123         Closure mixinAspect = new Closure() {
124             public void execute(Object JavaDoc o) {
125                 ((ProxyTypeBuilder) o).addInterceptorFactory(interfaces,
126                     factory);
127             }
128         };
129         
130         mixinAspects.add(new ClassPointcutAspect(classPointcut, mixinAspect));
131     }
132     
133     /**
134      * Convenience method. Uses all interfaces implemented by mixin class.
135      */

136     public void mixin(ClassPointcut classPointcut, Class JavaDoc mixinClass,
137             Closure initializer)
138             throws ConfigurationException {
139         List JavaDoc classes = Classes.getAllInterfacesAsList(mixinClass);
140         classes.removeAll(IGNORED_INTERFACES);
141         mixin(classPointcut,
142             (Class JavaDoc[]) classes.toArray(new Class JavaDoc[classes.size()]),
143                 mixinClass, initializer);
144     }
145
146     /**
147      * Adds custom mixin factory to classes picked by pointcut.
148      */

149     public void mixin(ClassPointcut classPointcut, final Class JavaDoc[] interfaces,
150             MixinFactory mixinFactory)
151             throws ConfigurationException {
152         interfaces(classPointcut, interfaces);
153
154         final InterceptorFactory factory =
155             new MixinFactoryInterceptorFactory(mixinFactory);
156         Closure mixinAspect = new Closure() {
157             public void execute(Object JavaDoc o) {
158                 ((ProxyTypeBuilder) o).addInterceptorFactory(interfaces,
159                     factory);
160             }
161         };
162
163         mixinAspects.add(new ClassPointcutAspect(classPointcut, mixinAspect));
164     }
165
166     //********************************************************************
167
// interceptor methods.
168
//********************************************************************
169

170     /**
171      * Adds method interceptor instance to methods picked by pointcuts. The
172      * interceptor instance may be shared between multiple proxies.
173      * Supports {@link ProxyAware}.
174      *
175      * @param classPointcut Picks target classes.
176      * @param methodPointcut Picks methods to add interceptor to.
177      * @param interceptor Interceptor instance.
178      */

179     public void interceptor(ClassPointcut classPointcut,
180             final MethodPointcut methodPointcut, Interceptor interceptor)
181             throws ConfigurationException {
182         interceptor(classPointcut, methodPointcut,
183             new SingletonInterceptorFactory(interceptor));
184     }
185
186     /**
187      * Adds method interceptor instance to methods picked by pointcuts.
188      * Creates an interceptor instance per proxy instance.
189      * Supports {@link ProxyAware}. Creates interceptor instances using default
190      * constructor. Executes <code>initializer</code> against
191      * interceptor instance after creation and before usage.
192      *
193      * @param classPointcut Picks target classes.
194      * @param methodPointcut Picks methods to add interceptor to.
195      * @param interceptorClass Interceptor class.
196      * @param initializer Code that initializes mixin instance. Can be
197      * <code>null</code>.
198      */

199     public void interceptor(ClassPointcut classPointcut,
200             MethodPointcut methodPointcut, Class JavaDoc interceptorClass,
201             Closure initializer)
202             throws ConfigurationException {
203         interceptor(classPointcut, methodPointcut,
204             new PerInstanceInterceptorFactory(interceptorClass, initializer));
205     }
206
207     /**
208      * Adds custom interceptor factory to methods picked by pointcuts.
209      *
210      * @param classPointcut Picks target classes.
211      * @param methodPointcut Picks methods to add interceptor to.
212      * @param factory Interceptor factory.
213      */

214     public void interceptor(ClassPointcut classPointcut,
215             final MethodPointcut methodPointcut,
216             final InterceptorFactory factory)
217             throws ConfigurationException {
218         if (methodPointcut == null)
219             throw new ConfigurationException("Method set is null.");
220
221         Closure interceptionAspect = new Closure() {
222             public void execute(Object JavaDoc o) {
223                 ((ProxyTypeBuilder) o).addInterceptorFactory(
224                     methodPointcut, factory);
225             }
226         };
227
228         this.interceptionAspects.add(
229                 new ClassPointcutAspect(classPointcut, interceptionAspect));
230     }
231
232     /**
233      * Sets <code>Proxy</code> if <code>o</code> is <code>ProxyAware</code>.
234      */

235     static void handleProxyAware(Object JavaDoc o, Proxy proxy) {
236         if (o instanceof ProxyAware)
237             ((ProxyAware) o).setProxy(proxy);
238     }
239
240     static class ClassPointcutAspect implements Closure {
241
242         private ClassPointcut classPointcut;
243         private Closure aspect;
244     
245         ClassPointcutAspect(ClassPointcut classPointcut, Closure aspect) {
246             if (classPointcut == null)
247                 throw new ConfigurationException("ClassPointcut is null.");
248             if (aspect == null)
249                 throw new ConfigurationException("Aspect is null.");
250
251             this.classPointcut = classPointcut;
252             this.aspect = aspect;
253         }
254     
255         public void execute(Object JavaDoc o) {
256             ProxyTypeBuilder builder = (ProxyTypeBuilder) o;
257             if (this.classPointcut.picks(builder.getProxiedClass()))
258                 this.aspect.execute(builder);
259         }
260     }
261     
262     //********************************************************************
263
// factory implementations.
264
//********************************************************************
265

266     static class SingletonInterceptorFactory implements InterceptorFactory,
267             Serializable JavaDoc {
268
269         static long serialVersionUID = 0;
270
271         Interceptor interceptor;
272     
273         SingletonInterceptorFactory(Interceptor interceptor) {
274             this.interceptor = interceptor;
275         }
276     
277         public Interceptor create(Proxy proxy) {
278             handleProxyAware(this.interceptor, proxy);
279             return this.interceptor;
280         }
281         
282         public Properties JavaDoc getProperties() {
283             Properties JavaDoc properties = new Properties JavaDoc();
284             properties.setProperty("advice", "method interceptor");
285             properties.setProperty("scope", "global");
286             properties.setProperty("class",
287                 this.interceptor.getClass().getName());
288             return properties;
289         }
290     }
291     
292     static class PerInstanceInterceptorFactory implements InterceptorFactory,
293             Serializable JavaDoc {
294
295         static long serialVersionUID = 0;
296
297         Class JavaDoc clazz;
298         Closure initializer;
299     
300         PerInstanceInterceptorFactory(Class JavaDoc clazz, Closure initializer) {
301             if (!Interceptor.class.isAssignableFrom(clazz))
302                 throw new ConfigurationException(clazz.getName() +
303                     " must implement " + Interceptor.class.getName() + ".");
304         
305             this.clazz = clazz;
306             this.initializer = initializer;
307         }
308     
309         public Interceptor create(Proxy proxy) {
310             Interceptor interceptor = (Interceptor) Classes.newInstance(clazz);
311             handleProxyAware(interceptor, proxy);
312             if (this.initializer != null)
313                 this.initializer.execute(interceptor);
314             return interceptor;
315         }
316
317         public Properties JavaDoc getProperties() {
318             Properties JavaDoc properties = new Properties JavaDoc();
319             properties.setProperty("advice", "method interceptor");
320             properties.setProperty("scope", "per-instance");
321             properties.setProperty("class", this.clazz.getName());
322             if (this.initializer != null)
323                 properties.setProperty("initializer",
324                         this.initializer.toString());
325
326             return properties;
327         }
328     }
329
330     static class MixinInterceptorFactory implements InterceptorFactory,
331             Serializable JavaDoc {
332
333         static long serialVersionUID = 0;
334
335         Class JavaDoc clazz;
336         Closure initializer;
337     
338         MixinInterceptorFactory(Class JavaDoc clazz, Closure initializer) {
339             this.clazz = clazz;
340         }
341     
342         public Interceptor create(Proxy proxy) {
343             try {
344                 Object JavaDoc mixin = this.clazz.newInstance();
345             
346                 // if the mixin is ProxyAware...
347
handleProxyAware(mixin, proxy);
348             
349                 // initialize the mixin.
350
if (this.initializer != null)
351                     this.initializer.execute(mixin);
352             
353                 return new MixinInterceptor(mixin);
354             }
355             catch (Exception JavaDoc e) {
356                 throw NestedException.wrap(e);
357             }
358         }
359
360         public Properties JavaDoc getProperties() {
361             Properties JavaDoc properties = new Properties JavaDoc();
362             properties.setProperty("advice", "mixin");
363             properties.setProperty("class", this.clazz.getName());
364             if (this.initializer != null)
365                 properties.setProperty("initializer",
366                     this.initializer.toString());
367     
368             return properties;
369         }
370     }
371
372     static class MixinInterceptor implements Interceptor, Serializable JavaDoc {
373         
374         static long serialVersionUID = 0;
375         
376         Object JavaDoc mixin;
377         
378         MixinInterceptor(Object JavaDoc mixin) {
379             this.mixin = mixin;
380         }
381         
382         public Object JavaDoc intercept(Invocation invocation) throws Throwable JavaDoc {
383             return Classes.invoke(this.mixin, invocation.getMethod(),
384                     invocation.getArguments());
385         }
386     }
387     
388     static class MixinFactoryInterceptorFactory implements InterceptorFactory,
389             Serializable JavaDoc {
390         
391         static long serialVersionUID = 0;
392
393         MixinFactory mixinFactory;
394         
395         MixinFactoryInterceptorFactory(MixinFactory mixinFactory) {
396             this.mixinFactory = mixinFactory;
397         }
398
399         public Interceptor create(Proxy proxy) {
400             try {
401                 Object JavaDoc mixin = this.mixinFactory.create(proxy);
402                 return new MixinInterceptor(mixin);
403             }
404             catch (Exception JavaDoc e) {
405                 throw NestedException.wrap(e);
406             }
407         }
408
409         public Properties JavaDoc getProperties() {
410             Properties JavaDoc properties = new Properties JavaDoc();
411             properties.putAll(this.mixinFactory.getProperties());
412             properties.setProperty("advice", "mixin");
413             properties.setProperty("mixinFactory",
414                 this.mixinFactory.getClass().getName());
415     
416             return properties;
417         }
418     }
419     
420     static Aspects defaultInstance;
421     
422     /**
423      * Gets default instance.&#160;Defaults to
424      * {@link dynaop.bsh.BshAspects}. Specify alternate implementation class
425      * using "dynaop.aspects" system property. Creates <code>Aspects</code>
426      * instance using default constructor.
427      *
428      * @see dynaop.bsh.BshAspects BshAspects for configuration
429      * instructions.
430      */

431     public static synchronized Aspects getInstance() {
432         if (defaultInstance == null) {
433             String JavaDoc className = System.getProperty("dynaop.aspects",
434                 DEFAULT_ASPECTS);
435             try {
436                 defaultInstance =
437                     (Aspects) Classes.forName(className).newInstance();
438             }
439             catch (Exception JavaDoc e) {
440                 throw NestedException.wrap(e);
441             }
442         }
443
444         return defaultInstance;
445     }
446 }
447
Popular Tags