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
110 return invokeAdvice(adviceIndex, joinPoint, new int[]{-1});
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) {
227
228
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 }