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.ContextClassLoader;
11 import org.codehaus.aspectwerkz.CrossCuttingInfo;
12 import org.codehaus.aspectwerkz.DeploymentModel;
13 import org.codehaus.aspectwerkz.Mixin;
14 import org.codehaus.aspectwerkz.definition.IntroductionDefinition;
15 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
16
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationTargetException;
19
20 /***
21 * Interface+Implementation Introduction <p/>This represents the inner class mixin based implementation in the system
22 *
23 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
24 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
25 */
26 public class Introduction implements Mixin {
27 private static final int MIXIN_CONSTRUCTION_TYPE_UNKNOWN = 0;
28
29 private static final int MIXIN_CONSTRUCTION_TYPE_DEFAULT = 1;
30
31 private static final int MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO = 2;
32
33 private static final Object[] ARRAY_WITH_CROSS_CUTTING_INFO = new Object[1];
34
35 /***
36 * An empty <code>Object</code> array.
37 */
38 public static final Object[] EMPTY_OBJECT_ARRAY = new Object[] {};
39
40 /***
41 * Mixin name
42 */
43 private String m_name;
44
45 /***
46 * Mixin implementation as aspect inner class Note: when swapped the impl can be an autonomous class
47 */
48 private Class m_mixinImplClass;
49
50 /***
51 * Mixin implementation as aspect inner class Note: when swapped the impl can be an autonomous class
52 */
53 private Object m_mixinImpl;
54
55 /***
56 * The constructor for the mixin.
57 */
58 private Constructor m_mixinConstructor;
59
60 /***
61 * The container for the introduction (single per JVM)
62 */
63 private IntroductionContainer m_container;
64
65 /***
66 * The cross-cutting info for the mixin.
67 */
68 private CrossCuttingInfo m_crossCuttingInfo;
69
70 /***
71 * Defintion to which this mixin relates
72 */
73 private IntroductionDefinition m_definition;
74
75 /***
76 * Holds the deployment model. The deployment model of an introduction is tight to the aspect deployment model that
77 * defines it
78 */
79 protected int m_deploymentModel;
80
81 /***
82 * The mixin construction type.
83 */
84 private int m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_UNKNOWN;
85
86 /***
87 * Create a new introduction
88 *
89 * @param name of this introduction - by convention the AspectClassFQN $ InnerClass
90 * @param implClass
91 * @param crossCuttingInfo which defines this mixin
92 * @param definition
93 */
94 public Introduction(final String name,
95 final Class implClass,
96 final CrossCuttingInfo crossCuttingInfo,
97 final IntroductionDefinition definition) {
98 m_name = name;
99 m_crossCuttingInfo = crossCuttingInfo;
100 m_definition = definition;
101 m_mixinImplClass = implClass;
102 m_mixinConstructor = findConstructor();
103 ARRAY_WITH_CROSS_CUTTING_INFO[0] = m_crossCuttingInfo;
104
105
106
107
108
109
110
111
112
113
114
115
116
117 if (definition.getDeploymentModel() == null) {
118 m_deploymentModel = m_crossCuttingInfo.getDeploymentModel();
119 } else {
120 int model = DeploymentModel.getDeploymentModelAsInt(definition.getDeploymentModel());
121 if (DeploymentModel.isMixinDeploymentModelCompatible(model, m_crossCuttingInfo.getDeploymentModel())) {
122 m_deploymentModel = model;
123 } else {
124 throw new RuntimeException("could no create mixin from aspect: incompatible deployment models : mixin "
125 + DeploymentModel.getDeploymentModelAsString(model)
126 + " with aspect "
127 + DeploymentModel.getDeploymentModelAsString(m_crossCuttingInfo.getDeploymentModel()));
128 }
129 }
130 }
131
132 /***
133 * Clone the prototype Introduction.
134 *
135 * @param prototype introduction
136 * @param crossCuttingInfo the cross-cutting info
137 * @return new introduction instance
138 */
139 public static Introduction newInstance(final Introduction prototype, final CrossCuttingInfo crossCuttingInfo) {
140 Introduction introduction = new Introduction(
141 prototype.m_name,
142 prototype.m_mixinImplClass,
143 crossCuttingInfo,
144 prototype.m_definition);
145
146
147 return introduction;
148 }
149
150 /***
151 * Creates a new mixin instance.
152 */
153 public void createMixin() {
154 try {
155 switch (m_mixinConstructionType) {
156 case MIXIN_CONSTRUCTION_TYPE_DEFAULT:
157 m_mixinImpl = m_mixinConstructor.newInstance(EMPTY_OBJECT_ARRAY);
158 break;
159 case MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO:
160 m_mixinImpl = m_mixinConstructor.newInstance(ARRAY_WITH_CROSS_CUTTING_INFO);
161 break;
162 default:
163 throw new RuntimeException(
164 "mixin ["
165 + m_mixinImplClass.getName()
166 + "] does not have a valid constructor (either default no-arg or one that takes a CrossCuttingInfo type as its only parameter)");
167 }
168 } catch (InstantiationException e) {
169 throw new WrappedRuntimeException(e);
170 } catch (IllegalAccessException e) {
171 throw new WrappedRuntimeException(e);
172 } catch (InvocationTargetException e) {
173 throw new WrappedRuntimeException(e.getTargetException());
174 }
175 }
176
177 /***
178 * Set the container.
179 *
180 * @param container
181 */
182 public void setContainer(final IntroductionContainer container) {
183 m_container = container;
184 }
185
186 /***
187 * Returns the cross-cutting info.
188 *
189 * @return the cross-cutting info.
190 */
191 public CrossCuttingInfo getCrossCuttingInfo() {
192 return m_crossCuttingInfo;
193 }
194
195 /***
196 * Returns the definition.
197 *
198 * @return definition related to this introduction
199 */
200 public IntroductionDefinition getIntroductionDefinition() {
201 return m_definition;
202 }
203
204 /***
205 * Returns the name of the mixin.
206 *
207 * @return the name
208 */
209 public String getName() {
210 return m_name;
211 }
212
213 /***
214 * Returns the mixin deployment model.
215 *
216 * @return the deployment model
217 */
218 public int getDeploymentModel() {
219 return m_deploymentModel;
220 }
221
222 /***
223 * Sets the deployment model.
224 *
225 * @param deploymentModel the deployment model
226 */
227 public void setDeploymentModel(final int deploymentModel) {
228 m_deploymentModel = deploymentModel;
229 }
230
231 /***
232 * Invokes the method with the index specified. Invoked by methods without any parameters (slight performance gain
233 * since we are saving us one array creation).
234 *
235 * @param methodIndex the method index
236 * @param callingObject a reference to the calling object
237 * @return the result from the invocation
238 */
239 public Object invokeMixin(final int methodIndex, final Object callingObject) throws Throwable {
240 return invokeMixin(methodIndex, EMPTY_OBJECT_ARRAY, callingObject);
241 }
242
243 /***
244 * Invokes an introduced method with the index specified.
245 *
246 * @param methodIndex the method index
247 * @param parameters the parameters for the invocation
248 * @param callingObject a reference to the calling object
249 * @return the result from the invocation
250 */
251 public Object invokeMixin(final int methodIndex, final Object[] parameters, final Object callingObject) throws Throwable {
252 Object result = null;
253 switch (m_deploymentModel) {
254 case DeploymentModel.PER_JVM:
255 result = m_container.invokeIntroductionPerJvm(methodIndex, parameters);
256 break;
257 case DeploymentModel.PER_CLASS:
258 result = m_container.invokeIntroductionPerClass(callingObject, methodIndex, parameters);
259 break;
260 case DeploymentModel.PER_INSTANCE:
261 result = m_container.invokeIntroductionPerInstance(callingObject, methodIndex, parameters);
262 break;
263 case DeploymentModel.PER_THREAD:
264 result = m_container.invokeIntroductionPerThread(methodIndex, parameters);
265 break;
266 default:
267 throw new RuntimeException("invalid deployment model: " + m_crossCuttingInfo.getDeploymentModel());
268 }
269 return result;
270 }
271
272 /***
273 * Returns the implementation class name for the mixin.
274 *
275 * @return the implementation class name for the mixin
276 */
277 public String getImplementationClassName() {
278 return m_mixinImplClass.getName();
279 }
280
281 /***
282 * Returns the implementation object for the mixin.
283 *
284 * @return the implementation for the mixin
285 */
286 public Class getImplementationClass() {
287 return m_mixinImplClass;
288 }
289
290 /***
291 * Returns the implementation object for the mixin.
292 *
293 * @return the implementation for the mixin
294 */
295 public Object getImplementation() {
296 return m_mixinImpl;
297 }
298
299 /***
300 * Swaps the current introduction implementation.
301 *
302 * @param className the class name of the new implementation
303 */
304 public void swapImplementation(final String className) {
305 if (className == null) {
306 throw new IllegalArgumentException("class name can not be null");
307 }
308 try {
309 Class newImplClass = ContextClassLoader.loadClass(className);
310
311
312
313 m_container.swapImplementation(newImplClass);
314 } catch (Exception e) {
315 throw new WrappedRuntimeException(e);
316 }
317 }
318
319 /***
320 * Grabs the correct constructor for the mixin.
321 *
322 * @return the constructor for the mixin
323 */
324 private Constructor findConstructor() {
325 Constructor mixinConstructor = null;
326 Constructor[] constructors = m_mixinImplClass.getDeclaredConstructors();
327 for (int i = 0; i < constructors.length; i++) {
328 Constructor constructor = constructors[i];
329 Class[] parameterTypes = constructor.getParameterTypes();
330 if (parameterTypes.length == 0) {
331 m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_DEFAULT;
332 mixinConstructor = constructor;
333 } else if ((parameterTypes.length == 1)
334 && parameterTypes[0].getName().equals(CrossCuttingInfo.class.getName())) {
335 m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO;
336 mixinConstructor = constructor;
337 break;
338 }
339 }
340 if (m_mixinConstructionType == MIXIN_CONSTRUCTION_TYPE_UNKNOWN) {
341 throw new RuntimeException(
342 "mixin ["
343 + m_mixinImplClass.getName()
344 + "] does not have a valid constructor (either default no-arg or one that takes a CrossCuttingInfo type as its only parameter)");
345 }
346 return mixinConstructor;
347 }
348
349 /***
350 * Swap the implementation of the mixin represented by this Introduction wrapper.
351 *
352 * @param newImplClass
353 */
354 void swapImplementation(final Class newImplClass) {
355 m_mixinImplClass = newImplClass;
356 m_mixinConstructor = findConstructor();
357 createMixin();
358 }
359 }