KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tirsen > nanning > AspectInstance


1 /*
2  * Nanning Aspects
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  * (C) 2003 Jon Tirsen
7  */

8 package com.tirsen.nanning;
9
10 import java.io.IOException JavaDoc;
11 import java.io.ObjectInputStream JavaDoc;
12 import java.io.Serializable JavaDoc;
13 import java.lang.reflect.InvocationHandler JavaDoc;
14 import java.lang.reflect.Method JavaDoc;
15 import java.lang.reflect.Proxy JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Collections JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.LinkedHashSet JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.Set JavaDoc;
25
26 import org.apache.commons.collections.CollectionUtils;
27 import org.apache.commons.collections.Transformer;
28
29 /**
30  * The central concept of the Nanning Core, contains mixins.
31  * Use like this:
32  * <pre><code>
33  AspectInstance aspectInstance = new AspectInstance();
34  MixinInstance mixinInstance = new MixinInstance();
35  mixinInstance.setInterfaceClass(Intf.class);
36  mixinInstance.addInterceptor(new MockInterceptor());
37  mixinInstance.addInterceptor(new NullInterceptor());
38  mixinInstance.setTarget(new Impl());
39  aspectInstance.addMixin(mixinInstance);
40  </pre></code>
41  *
42  * <!-- $Id: AspectInstance.java,v 1.50 2003/06/20 11:53:58 tirsen Exp $ -->
43  *
44  * @author $Author: tirsen $
45  * @version $Revision: 1.50 $
46  */

47 public final class AspectInstance implements InvocationHandler JavaDoc, Serializable JavaDoc {
48     static final long serialVersionUID = 5462785783512485056L;
49
50     private Map JavaDoc mixins = new HashMap JavaDoc();
51     private List JavaDoc mixinsList = new ArrayList JavaDoc();
52     private Class JavaDoc classIdentifier;
53
54     private Object JavaDoc proxy;
55     private transient List JavaDoc constructionInterceptors = new ArrayList JavaDoc();
56     private transient AspectFactory aspectFactory;
57
58     public AspectInstance() {
59     }
60
61     public AspectInstance(AspectFactory aspectFactory, Class JavaDoc classIdentifier) {
62         this.aspectFactory = aspectFactory;
63         this.classIdentifier = classIdentifier;
64     }
65
66     public AspectInstance(Class JavaDoc classIdentifier) {
67         this.classIdentifier = classIdentifier;
68     }
69
70     public Object JavaDoc createProxy(boolean runConstructionInterceptors) {
71         if (proxy == null) {
72             Set JavaDoc interfaces = getInterfaceClasses();
73             proxy = Proxy.newProxyInstance(getClass().getClassLoader(),
74                                            (Class JavaDoc[]) interfaces.toArray(new Class JavaDoc[0]),
75                                            this);
76         }
77         if (runConstructionInterceptors) {
78             proxy = executeConstructionInterceptors(proxy);
79         }
80         return proxy;
81     }
82
83     private Set JavaDoc getInterfaceClasses() {
84         Set JavaDoc interfaces = new HashSet JavaDoc(CollectionUtils.collect(mixins.values(), new Transformer() {
85             public Object JavaDoc transform(Object JavaDoc o) {
86                 return ((MixinInstance) o).getInterfaceClass();
87             }
88         }));
89         if (classIdentifier != null) {
90             interfaces.add(classIdentifier);
91         }
92         return interfaces;
93     }
94
95     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
96
97         Class JavaDoc interfaceClass = method.getDeclaringClass();
98
99         if (interfaceClass != Object JavaDoc.class) {
100             Object JavaDoc prevThis = Aspects.getThis();
101             try {
102                 Aspects.setThis(proxy);
103                 MixinInstance mixin = getMixinForInterface(interfaceClass);
104                 return mixin.invokeMethod(proxy, method, args);
105             } finally {
106                 Aspects.setThis(prevThis);
107             }
108
109         } else {
110             // for methods defined on Object:
111
// change all proxies into AspectInstances and the call this aspect instance
112
if (args != null) {
113                 for (int i = 0; i < args.length; i++) {
114                     Object JavaDoc arg = args[i];
115                     if (Aspects.isAspectObject(arg)) {
116                         args[i] = Aspects.getAspectInstance(arg);
117                     }
118                 }
119             }
120             return method.invoke(this, args);
121         }
122     }
123
124     Object JavaDoc getTarget(Class JavaDoc interfaceClass) {
125         MixinInstance interfaceInstance = getMixinForInterface(interfaceClass);
126         return interfaceInstance.getTarget();
127     }
128
129     Set JavaDoc getInterceptors(Class JavaDoc interfaceClass) {
130         MixinInstance interfaceInstance = getMixinForInterface(interfaceClass);
131         return interfaceInstance.getAllInterceptors();
132     }
133
134     /**
135      * Returns the mixin with the specified interface.
136      * @param interfaceClass
137      * @return
138      */

139     public MixinInstance getMixinForInterface(Class JavaDoc interfaceClass) {
140         MixinInstance mixinInstance = (MixinInstance) mixins.get(interfaceClass);
141         assert mixinInstance != null : "there is no mixin for interface " + interfaceClass + " mixins were " + mixins;
142         return mixinInstance;
143     }
144
145     public boolean hasMixinForInterface(Class JavaDoc interfaceClass) {
146         return mixins.containsKey(interfaceClass);
147     }
148
149     public void setTarget(Class JavaDoc interfaceClass, Object JavaDoc target) {
150         MixinInstance mixinInstance = getMixinForInterface(interfaceClass);
151         mixinInstance.setTarget(target);
152     }
153
154     public Object JavaDoc[] getTargets() {
155         return CollectionUtils.collect(mixinsList, new Transformer() {
156             public Object JavaDoc transform(Object JavaDoc o) {
157                 return ((MixinInstance) o).getTarget();
158             }
159         }).toArray();
160     }
161
162     private void readObject(ObjectInputStream JavaDoc objectInputStream) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
163         objectInputStream.defaultReadObject();
164         AspectFactory currentAspectFactory = Aspects.getCurrentAspectFactory();
165         assert currentAspectFactory != null : "context AspectFactory not specified, it is not possible to deserialize " + this;
166         aspectFactory = currentAspectFactory;
167         aspectFactory.reinitialize(this);
168     }
169
170     public Class JavaDoc getClassIdentifier() {
171         return classIdentifier;
172     }
173
174     /**
175      * Adds a mixin.
176      * @param mixin
177      */

178     public void addMixin(MixinInstance mixin) {
179         assert proxy == null : "Can't addMixin mixins when proxy has been created.";
180         Class JavaDoc interfaceClass = mixin.getInterfaceClass();
181         bindMixinToInterface(interfaceClass, mixin);
182         mixinsList.add(mixin);
183     }
184
185
186     public void setMixins(List JavaDoc mixinsList) {
187         this.mixinsList = mixinsList;
188         mixins.clear();
189         for (Iterator JavaDoc i = mixinsList.iterator(); i.hasNext();) {
190             MixinInstance mixinInstance = (MixinInstance) i.next();
191             bindMixinToInterface(mixinInstance.getInterfaceClass(), mixinInstance);
192         }
193     }
194     
195     /**
196      * Binds the mixin to the specified interface and all of it's superclasses, overrides any other bindings
197      * to that interface (and superclasses).
198      * @param interfaceClass
199      * @param mixinInstance
200      */

201     private void bindMixinToInterface(Class JavaDoc interfaceClass, MixinInstance mixinInstance) {
202         mixins.put(interfaceClass, mixinInstance);
203         Class JavaDoc superclass = interfaceClass.getSuperclass();
204         if (superclass != null) {
205             bindMixinToInterface(superclass, mixinInstance);
206         }
207         Class JavaDoc[] interfaces = interfaceClass.getInterfaces();
208         if (interfaces != null) {
209             for (int i = 0; i < interfaces.length; i++) {
210                 Class JavaDoc anInterface = interfaces[i];
211                 bindMixinToInterface(anInterface, mixinInstance);
212             }
213         }
214     }
215
216     /**
217      * Returns all the interceptors referenced by this aspect instance. That is all interceptors of all methods of
218      * all mixins, invluding the construction-interceptors.
219      * @return
220      */

221     public Set JavaDoc getAllInterceptors() {
222         Set JavaDoc result = new LinkedHashSet JavaDoc();
223         if (constructionInterceptors != null) {
224             result.addAll(constructionInterceptors);
225         }
226
227         for (Iterator JavaDoc mixinIterator = mixinsList.iterator(); mixinIterator.hasNext();) {
228             MixinInstance mixinInstance = (MixinInstance) mixinIterator.next();
229             Set JavaDoc allInterceptors = mixinInstance.getAllInterceptors();
230             for (Iterator JavaDoc interceptorIterator = allInterceptors.iterator(); interceptorIterator.hasNext();) {
231                 Interceptor interceptor = (Interceptor) interceptorIterator.next();
232                 result.add(interceptor);
233             }
234         }
235         return result;
236     }
237
238     /**
239      * Returns the interceptors of the specified method, searches in the mixin for the interface that the method
240      * has been declared on.
241      * @param method
242      * @return
243      */

244     public List JavaDoc getInterceptorsForMethod(Method JavaDoc method) {
245         return getMixinForInterface(method.getDeclaringClass()).getInterceptorsForMethod(method);
246     }
247
248     /**
249      * Returns the AspectFactory used to create and configure this AspectInstance (if set by the AspectFactory).
250      * @return
251      */

252     public final AspectFactory getAspectFactory() {
253         return aspectFactory;
254     }
255
256     public final class ConstructionInvocationImpl implements ConstructionInvocation {
257         private Object JavaDoc proxy;
258         private Class JavaDoc interfaceClass;
259
260         public ConstructionInvocationImpl(Object JavaDoc proxy, Class JavaDoc interfaceClass) {
261             this.proxy = proxy;
262             this.interfaceClass = interfaceClass;
263         }
264
265         public Object JavaDoc getProxy() {
266             return proxy;
267         }
268
269         public Object JavaDoc getTarget() {
270             return Aspects.getTarget(proxy, interfaceClass);
271         }
272
273         public void setTarget(Object JavaDoc target) {
274             Aspects.setTarget(proxy, interfaceClass, target);
275         }
276     }
277
278     public Object JavaDoc getProxy() {
279         return createProxy(true);
280     }
281
282     private Object JavaDoc executeConstructionInterceptors(Object JavaDoc proxy) {
283         Object JavaDoc prevThis = Aspects.getThis();
284         try {
285             Aspects.setThis(proxy);
286
287             if (constructionInterceptors != null) {
288                 for (Iterator JavaDoc iterator = constructionInterceptors.iterator(); iterator.hasNext();) {
289                     ConstructionInterceptor constructionInterceptor = (ConstructionInterceptor) iterator.next();
290                     if (constructionInterceptor.interceptsConstructor(getClassIdentifier())) {
291                         proxy = constructionInterceptor.construct(new ConstructionInvocationImpl(proxy, getClassIdentifier()));
292                     }
293                 }
294             }
295
296         } finally {
297             constructionInterceptors = null;
298             Aspects.setThis(prevThis);
299         }
300         return proxy;
301     }
302
303     public String JavaDoc toString() {
304         if (mixinsList.size() == 1) {
305             return "aspect{" + mixinsList.get(0).toString() + "}";
306         }
307         return "aspect{class=" + classIdentifier + "," +
308                 "mixins=" + mixinsList + "}";
309     }
310
311     /**
312      * Returns all mixins defined on this AspectInstance.
313      * @return
314      */

315     public List JavaDoc getMixins() {
316         return Collections.unmodifiableList(mixinsList);
317     }
318
319     /**
320      * Adds a ConstructionInterceptor, the interceptor will be invoked when creating the proxy in {@link #getProxy()}.
321      */

322     public void addConstructionInterceptor(ConstructionInterceptor constructionInterceptor) {
323         if (constructionInterceptors == null) {
324             constructionInterceptors = new ArrayList JavaDoc();
325         }
326         constructionInterceptors.add(constructionInterceptor);
327     }
328
329     public void addInterceptor(Interceptor interceptor) {
330         for (Iterator JavaDoc iterator = mixinsList.iterator(); iterator.hasNext();) {
331             MixinInstance mixin = (MixinInstance) iterator.next();
332             mixin.addInterceptor(interceptor);
333         }
334     }
335 }
336
Popular Tags