View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.aspect;
9   
10  import org.codehaus.aspectwerkz.CrossCuttingInfo;
11  import org.codehaus.aspectwerkz.DeploymentModel;
12  import org.codehaus.aspectwerkz.exception.DefinitionException;
13  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
14  import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
15  import org.codehaus.aspectwerkz.joinpoint.MethodRtti;
16  import org.codehaus.aspectwerkz.joinpoint.management.JoinPointBase;
17  import org.codehaus.aspectwerkz.transform.ReflectHelper;
18  import java.lang.reflect.InvocationTargetException;
19  import java.lang.reflect.Method;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.WeakHashMap;
24  
25  /***
26   * Abstract base class for the aspect container implementations.
27   * 
28   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
29   */
30  public abstract class AbstractAspectContainer implements AspectContainer {
31      public static final int ASPECT_CONSTRUCTION_TYPE_UNKNOWN = 0;
32      public static final int ASPECT_CONSTRUCTION_TYPE_DEFAULT = 1;
33      public static final int ASPECT_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO = 2;
34      public static final Object[] EMPTY_OBJECT_ARRAY = new Object[] {};
35  
36      /***
37       * The aspect construction type.
38       */
39      protected int m_constructionType = ASPECT_CONSTRUCTION_TYPE_UNKNOWN;
40  
41      /***
42       * Introduction container containing introduction declared by this aspect, keys by introduction names
43       */
44      protected final Map m_introductionContainers = new HashMap();
45  
46      /***
47       * The cross-cutting info prototype.
48       */
49      protected final CrossCuttingInfo m_infoPrototype;
50  
51      /***
52       * An array with the single cross-cutting info, needed to save one array creation per invocation.
53       */
54      protected final Object[] arrayWithSingleCrossCuttingInfo = new Object[1];
55  
56      /***
57       * The aspect instance prototype.
58       */
59      protected final Object m_aspectPrototype;
60  
61      /***
62       * Holds a reference to the sole per JVM aspect instance.
63       */
64      protected Object m_perJvm;
65  
66      /***
67       * Holds references to the per class aspect instances.
68       */
69      protected final Map m_perClass = new WeakHashMap();
70  
71      /***
72       * Holds references to the per instance aspect instances.
73       */
74      protected final Map m_perInstance = new WeakHashMap();
75  
76      /***
77       * Holds references to the per thread aspect instances.
78       */
79      protected final Map m_perThread = new WeakHashMap();
80  
81      /***
82       * The advice repository.
83       */
84      protected Method[] m_adviceRepository = new Method[0];
85  
86      /***
87       * Creates a new aspect container strategy.
88       * 
89       * @param crossCuttingInfo the cross-cutting info
90       */
91      public AbstractAspectContainer(final CrossCuttingInfo crossCuttingInfo) {
92          if (crossCuttingInfo == null) {
93              throw new IllegalArgumentException("cross-cutting info can not be null");
94          }
95          m_infoPrototype = crossCuttingInfo;
96          arrayWithSingleCrossCuttingInfo[0] = m_infoPrototype;
97          m_aspectPrototype = createAspect();
98          createAdviceRepository();
99      }
100 
101     /***
102      * Invokes an advice with the index specified.
103      * 
104      * @param adviceIndex the advice index
105      * @param joinPoint the join point
106      * @return the result from the invocation
107      */
108     public Object invokeAdvice(final int adviceIndex, final JoinPoint joinPoint) throws Throwable {
109         //throw new UnsupportedOperationException("deprecated for args() support");
110         return invokeAdvice(adviceIndex, joinPoint, new int[]{-1});//cflow backward compatible
111     }
112 
113     public Object invokeAdvice(final int adviceIndex, final JoinPoint joinPoint, int[] methodToArgIndexes) throws Throwable {
114         Object result = null;
115         switch (m_infoPrototype.getDeploymentModel()) {
116             case DeploymentModel.PER_JVM:
117                 result = invokeAdvicePerJvm(adviceIndex, joinPoint, methodToArgIndexes);
118                 break;
119             case DeploymentModel.PER_CLASS:
120                 result = invokeAdvicePerClass(adviceIndex, joinPoint, methodToArgIndexes);
121                 break;
122             case DeploymentModel.PER_INSTANCE:
123                 result = invokeAdvicePerInstance(adviceIndex, joinPoint, methodToArgIndexes);
124                 break;
125             case DeploymentModel.PER_THREAD:
126                 result = invokeAdvicePerThread(adviceIndex, joinPoint, methodToArgIndexes);
127                 break;
128             default:
129                 throw new RuntimeException("invalid deployment model: " + m_infoPrototype.getDeploymentModel());
130         }
131         return result;
132     }
133 
134     /***
135      * Returns a specific advice by index.
136      * 
137      * @param index the index
138      * @return the advice
139      */
140     public Method getAdvice(final int index) {
141         if (index < 0) {
142             throw new IllegalArgumentException("advice index can not be less than 0");
143         }
144         return m_adviceRepository[index];
145     }
146 
147     /***
148      * Returns the cross-cutting info.
149      * 
150      * @return the cross-cutting info
151      */
152     public CrossCuttingInfo getCrossCuttingInfo() {
153         return m_infoPrototype;
154     }
155 
156     /***
157      * Invokes the advice method on a per JVM basis.
158      * 
159      * @param adviceIndex the advice index
160      * @param joinPoint the join point
161      * @param methodToArgIndexes indexes of args to pass to the advice
162      * @return the result from the method invocation
163      */
164     private Object invokeAdvicePerJvm(final int adviceIndex, final JoinPoint joinPoint, int[] methodToArgIndexes) throws Throwable {
165         Object result;
166         try {
167             createPerJvmAspect();
168             Method method = m_adviceRepository[adviceIndex];
169             result = method.invoke(m_perJvm, ((JoinPointBase)joinPoint).extractArguments(methodToArgIndexes));
170         } catch (ArrayIndexOutOfBoundsException e) {
171             throw new DefinitionException("advices for aspect ["
172                 + m_infoPrototype.getAspectDefinition().getName()
173                 + "] are not properly defined");
174         } catch (InvocationTargetException e) {
175             throw e.getTargetException();
176         } catch (Exception e) {
177             System.err.println("advice Method : " + m_adviceRepository[adviceIndex].toString());
178             System.err.println("args map: ");
179             for (int i = 0; i < methodToArgIndexes.length; i++) {
180                 System.err.println("\t" + methodToArgIndexes[i]);
181             }
182             throw new WrappedRuntimeException(e);
183         }
184         return result;
185     }
186 
187     /***
188      * Invokes the advice method on a per class basis.
189      * 
190      * @param adviceIndex the advice index
191      * @param joinPoint the join point
192      * @param methodToArgIndexes indexes of args to pass to the advice
193      * @return the result from the method invocation
194      */
195     private Object invokeAdvicePerClass(final int adviceIndex, final JoinPoint joinPoint, int[] methodToArgIndexes) throws Throwable {
196         final Class targetClass = joinPoint.getTargetClass();
197         Object result;
198         try {
199             createPerClassAspect(targetClass);
200             result = m_adviceRepository[adviceIndex].invoke(m_perClass.get(targetClass),
201                     ((JoinPointBase)joinPoint).extractArguments(methodToArgIndexes)
202             );
203         } catch (ArrayIndexOutOfBoundsException e) {
204             throw new DefinitionException("advices for aspect ["
205                 + m_infoPrototype.getAspectDefinition().getName()
206                 + "] are not properly defined");
207         } catch (InvocationTargetException e) {
208             throw e.getTargetException();
209         } catch (Exception e) {
210             throw new WrappedRuntimeException(e);
211         }
212         return result;
213     }
214 
215     /***
216      * Invokes the advice method on a per instance basis.
217      * 
218      * @param adviceIndex the advice index
219      * @param joinPoint the join point
220      * @param methodToArgIndexes indexes of args to pass to the advice
221      * @return the result from the method invocation
222      */
223     private Object invokeAdvicePerInstance(final int adviceIndex, final JoinPoint joinPoint, int[] methodToArgIndexes) throws Throwable {
224         Object result = null;
225         Object targetInstance = joinPoint.getTarget();
226         if (targetInstance == null) { // can be null if f.e. an aspect has deployment model
227             // perInstance and has caller
228             // side pointcuts defined
229             return invokeAdvicePerClass(adviceIndex, joinPoint, methodToArgIndexes);
230         }
231         try {
232             createPerInstanceAspect(targetInstance);
233             result = m_adviceRepository[adviceIndex].invoke(m_perInstance.get(targetInstance),
234                     ((JoinPointBase)joinPoint).extractArguments(methodToArgIndexes)
235             );
236         } catch (ArrayIndexOutOfBoundsException e) {
237             throw new DefinitionException("advices for aspect ["
238                 + m_infoPrototype.getAspectDefinition().getName()
239                 + "] are not properly defined");
240         } catch (InvocationTargetException e) {
241             throw e.getTargetException();
242         } catch (Exception e) {
243             throw new WrappedRuntimeException(e);
244         }
245         return result;
246     }
247 
248     /***
249      * Invokes the advice method on a per thread basis.
250      * 
251      * @param adviceIndex the advice index
252      * @param joinPoint the join point
253      * @param methodToArgIndexes indexes of args to pass to the advice
254      * @return the result from the method invocation
255      */
256     private Object invokeAdvicePerThread(final int adviceIndex, final JoinPoint joinPoint, int[] methodToArgIndexes) throws Throwable {
257         Object result;
258         try {
259             final Thread currentThread = Thread.currentThread();
260             createPerThreadAspect(currentThread);
261             Method method = m_adviceRepository[adviceIndex];
262             result = method.invoke(m_perThread.get(currentThread),
263                     ((JoinPointBase)joinPoint).extractArguments(methodToArgIndexes)
264             );
265         } catch (ArrayIndexOutOfBoundsException e) {
266             throw new DefinitionException("advices for aspect ["
267                 + m_infoPrototype.getAspectDefinition().getName()
268                 + "] are not properly defined");
269         } catch (InvocationTargetException e) {
270             throw e.getTargetException();
271         } catch (Exception e) {
272             throw new WrappedRuntimeException(e);
273         }
274         return result;
275     }
276 
277     /***
278      * Creates a new perJVM cross-cutting instance, if it already exists then return it.
279      * 
280      * @return the cross-cutting instance
281      */
282     public Object createPerJvmAspect() {
283         if (m_perJvm == null) {
284             m_perJvm = createAspect();
285         }
286         return m_perJvm;
287     }
288 
289     /***
290      * Creates a new perClass cross-cutting instance, if it already exists then return it.
291      * 
292      * @param callingClass
293      * @return the cross-cutting instance
294      */
295     public Object createPerClassAspect(final Class callingClass) {
296         synchronized (m_perClass) {
297             if (!m_perClass.containsKey(callingClass)) {
298                 m_perClass.put(callingClass, createAspect());
299             }
300         }
301         return m_perClass.get(callingClass);
302     }
303 
304     /***
305      * Creates a new perInstance cross-cutting instance, if it already exists then return it.
306      * 
307      * @param callingInstance
308      * @return the cross-cutting instance
309      */
310     public Object createPerInstanceAspect(final Object callingInstance) {
311         if (callingInstance == null) {
312             return m_perJvm;
313         }
314         synchronized (m_perInstance) {
315             if (!m_perInstance.containsKey(callingInstance)) {
316                 m_perInstance.put(callingInstance, createAspect());
317             }
318         }
319         return m_perInstance.get(callingInstance);
320     }
321 
322     /***
323      * Creates a new perThread cross-cutting instance, if it already exists then return it.
324      * 
325      * @param thread the thread for the aspect
326      * @return the cross-cutting instance
327      */
328     public Object createPerThreadAspect(final Thread thread) {
329         synchronized (m_perThread) {
330             if (!m_perThread.containsKey(thread)) {
331                 m_perThread.put(thread, createAspect());
332             }
333         }
334         return m_perThread.get(thread);
335     }
336 
337     /***
338      * Attach the introduction container to this aspect container to mirror the "aspect contains 0-n introduction"
339      * 
340      * @param name of the introduction
341      * @param introContainer introduction container
342      */
343     public void addIntroductionContainer(final String name, final IntroductionContainer introContainer) {
344         m_introductionContainers.put(name, introContainer);
345     }
346 
347     /***
348      * Returns the introduction container of given name (introduction name) or null if not linked.
349      * 
350      * @param name of the introduction
351      * @return introduction container
352      */
353     public IntroductionContainer getIntroductionContainer(final String name) {
354         return (IntroductionContainer) m_introductionContainers.get(name);
355     }
356 
357     /***
358      * Creates a repository for the advice methods.
359      */
360     protected void createAdviceRepository() {
361         synchronized (m_adviceRepository) {
362             List methodList = ReflectHelper.createCompleteSortedMethodList(m_infoPrototype.getAspectClass());
363             m_adviceRepository = new Method[methodList.size()];
364             for (int i = 0; i < m_adviceRepository.length; i++) {
365                 Method method = (Method) methodList.get(i);
366                 method.setAccessible(true);
367                 m_adviceRepository[i] = method;
368             }
369         }
370     }
371 
372     /***
373      * To be implemented by the concrete aspect containers. <p/>Should return a new aspect instance.
374      * 
375      * @return a new aspect instance
376      */
377     protected abstract Object createAspect();
378 
379 }