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.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         // handle deploymentModel dependancies
106         // defaults to Aspect deploymentModel
107         // else supported models are:
108         // Mixin Aspect
109         // perJVM perJVM
110         // perClass perJVM,perClass
111         // perInstance perJVM,perClass,perInstance
112         // perThread perThread
113         // todo all those checks should be done earlier
114         // (AspectC thought doclet inheritance might cause problem when inheritating compiled
115         // aspects without source
116         // code)
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         //AW-207//introduction.createMixin();
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); //todo pbly old
310             // impl.getClassLoader()
311             // would
312             // be safer
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 }