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.transform.delegation;
9
10 import org.codehaus.aspectwerkz.definition.SystemDefinition;
11 import org.codehaus.aspectwerkz.expression.ExpressionContext;
12 import org.codehaus.aspectwerkz.expression.PointcutType;
13 import org.codehaus.aspectwerkz.reflect.ClassInfo;
14 import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
15 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistClassInfo;
16 import org.codehaus.aspectwerkz.reflect.impl.javassist.JavassistConstructorInfo;
17 import org.codehaus.aspectwerkz.transform.Context;
18 import org.codehaus.aspectwerkz.transform.TransformationUtil;
19 import org.codehaus.aspectwerkz.transform.Transformer;
20 import org.codehaus.aspectwerkz.transform.TransformationConstants;
21 import org.codehaus.aspectwerkz.transform.TransformationConstants;
22
23 import java.util.Iterator;
24 import java.util.List;
25
26 import javassist.CannotCompileException;
27 import javassist.CtClass;
28 import javassist.CtConstructor;
29 import javassist.CtMethod;
30 import javassist.CtNewConstructor;
31 import javassist.NotFoundException;
32 import javassist.bytecode.CodeAttribute;
33
34 /***
35 * Advises constructor EXECUTION join points.
36 *
37 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
38 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
39 */
40 public class ConstructorExecutionTransformer implements Transformer {
41 /***
42 * The join point index.
43 */
44
45
46 /***
47 * Makes the member method transformations.
48 *
49 * @param context the transformation context
50 * @param klass the class set.
51 */
52 public void transform(final Context context, final Klass klass) throws Exception {
53 List definitions = context.getDefinitions();
54
55
56
57
58 for (Iterator it = definitions.iterator(); it.hasNext();) {
59 SystemDefinition definition = (SystemDefinition) it.next();
60 final CtClass ctClass = klass.getCtClass();
61 ClassInfo classInfo = JavassistClassInfo.getClassInfo(ctClass, context.getLoader());
62 if (classFilter(definition, new ExpressionContext(PointcutType.EXECUTION, classInfo, classInfo), ctClass)) {
63 continue;
64 }
65 final CtConstructor[] constructors = ctClass.getConstructors();
66 for (int i = 0; i < constructors.length; i++) {
67 CtConstructor constructor = constructors[i];
68 ConstructorInfo constructorInfo = JavassistConstructorInfo.getConstructorInfo(constructor, context
69 .getLoader());
70 ExpressionContext ctx = new ExpressionContext(PointcutType.EXECUTION, constructorInfo, constructorInfo);
71 if (constructorFilter(definition, ctx)) {
72 continue;
73 }
74 if (addPrefixToConstructor(ctClass, constructor)) {
75 context.markAsAdvised();
76 int constructorHash = JavassistHelper.calculateHash(constructor);
77 createWrapperConstructor(constructor, constructorHash, klass);
78 }
79 }
80 }
81
82
83
84 klass.flushJoinPointIndex();
85 }
86
87 /***
88 * Creates a wrapper constructor for the original constructor specified. This constructor has the same signature as
89 * the original constructor and catches the invocation for further processing by the framework before redirecting to
90 * the original constructor.
91 *
92 * @param originalConstructor the original constructor
93 * @param constructorHash the constructor hash
94 */
95 private void createWrapperConstructor(
96 final CtConstructor originalConstructor,
97 final int constructorHash,
98 final Klass klass) throws CannotCompileException, NotFoundException {
99 StringBuffer body = new StringBuffer();
100 body.append('{');
101 if (originalConstructor.getParameterTypes().length > 0) {
102 body.append("Object[] args = $args; ");
103 } else {
104 body.append("Object[] args = null; ");
105 }
106 body.append("Object nullObject = null;");
107 body.append("return ($r)");
108 body.append(TransformationConstants.JOIN_POINT_MANAGER_FIELD);
109 body.append('.');
110 body.append(TransformationConstants.PROCEED_WITH_EXECUTION_JOIN_POINT_METHOD);
111 body.append('(');
112 body.append(constructorHash);
113 body.append(',');
114 body.append(klass.getJoinPointIndex());
115 body.append(',');
116 body.append("args, this,");
117 body.append(TransformationConstants.JOIN_POINT_TYPE_CONSTRUCTOR_EXECUTION);
118 body.append("); }");
119 klass.incrementJoinPointIndex();
120 originalConstructor.setBody(body.toString());
121 }
122
123 /***
124 * @param ctClass the class
125 * @param constructor the current method
126 * @return the new prefixed constructor
127 */
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 /***
156 * Adds a prefix to the original constructor. To make it callable only from within the framework itself.
157 *
158 * @param ctClass the class
159 * @param constructor the current method
160 * @return false if the prefixed constructor was already existing
161 */
162 private boolean addPrefixToConstructor(final CtClass ctClass, final CtConstructor constructor) throws NotFoundException,
163 CannotCompileException {
164 int accessFlags = constructor.getModifiers();
165 CtClass[] parameterTypes = constructor.getParameterTypes();
166 CtClass[] newParameterTypes = new CtClass[parameterTypes.length + 1];
167 for (int i = 0; i < parameterTypes.length; i++) {
168 newParameterTypes[i] = parameterTypes[i];
169 }
170 newParameterTypes[parameterTypes.length] = ctClass.getClassPool().get(
171 TransformationConstants.JOIN_POINT_MANAGER_CLASS);
172 if (!JavassistHelper.hasConstructor(ctClass, newParameterTypes)) {
173 CtConstructor newConstructor = CtNewConstructor.make(
174 newParameterTypes,
175 constructor.getExceptionTypes(),
176 CtNewConstructor.PASS_NONE,
177 null,
178 CtMethod.ConstParameter.string(constructor.getSignature()),
179 ctClass);
180 newConstructor.setBody(constructor, null);
181 newConstructor.setModifiers(accessFlags);
182 CodeAttribute codeAttribute = newConstructor.getMethodInfo().getCodeAttribute();
183 codeAttribute.setMaxLocals(codeAttribute.getMaxLocals() + 1);
184 JavassistHelper.copyCustomAttributes(constructor, newConstructor);
185 ctClass.addConstructor(newConstructor);
186 return true;
187 } else {
188 return false;
189 }
190 }
191
192 /***
193 * Filters the classes to be transformed.
194 *
195 * @param definition the definition
196 * @param ctx the context
197 * @param ctClass the class to filter
198 * @return boolean true if the method should be filtered away
199 */
200 public static boolean classFilter(
201 final SystemDefinition definition,
202 final ExpressionContext ctx,
203 final CtClass ctClass) {
204 if (ctClass.isInterface()) {
205 return true;
206 }
207 String className = ctClass.getName().replace('/', '.');
208 if (definition.inExcludePackage(className)) {
209 return true;
210 }
211 if (!definition.inIncludePackage(className)) {
212 return true;
213 }
214 if (definition.isAdvised(ctx)) {
215 return false;
216 }
217 return true;
218 }
219
220 /***
221 * Filters the methods to be transformed.
222 *
223 * @param definition the definition
224 * @param ctx the context
225 * @return boolean
226 */
227 public static boolean constructorFilter(final SystemDefinition definition, final ExpressionContext ctx) {
228 if (definition.hasPointcut(ctx)) {
229 return false;
230 } else {
231 return true;
232 }
233 }
234 }