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.transform.Context;
12 import org.codehaus.aspectwerkz.transform.TransformationUtil;
13 import org.codehaus.aspectwerkz.transform.Transformer;
14 import org.codehaus.aspectwerkz.transform.TransformationConstants;
15
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21
22 import javassist.CannotCompileException;
23 import javassist.CtClass;
24 import javassist.CtMethod;
25 import javassist.CtNewMethod;
26 import javassist.Modifier;
27 import javassist.NotFoundException;
28
29 /***
30 * Prepare class for further hotswap for execution pointcut TODO support for constructor pointcuts TODO AOPC def model
31 *
32 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
33 */
34 public class PrepareTransformer implements Transformer {
35 /***
36 * Add the class static field, the joinpoint manager, and add method stubs
37 *
38 * @param context the transformation context
39 * @param klass the class set.
40 */
41 public void transform(final Context context, final Klass klass) throws NotFoundException, CannotCompileException {
42 List definitions = context.getDefinitions();
43
44
45 for (Iterator it = definitions.iterator(); it.hasNext();) {
46 SystemDefinition definition = (SystemDefinition) it.next();
47 final CtClass ctClass = klass.getCtClass();
48
49
50
51
52 if (classFilter(definition, ctClass)) {
53 continue;
54 }
55 System.out.println("preparing: " + ctClass.getName());
56
57
58
59
60 context.markAsPrepared();
61 final CtMethod[] methods = ctClass.getDeclaredMethods();
62
63
64 final List methodLookupList = new ArrayList();
65 for (int i = 0; i < methods.length; i++) {
66 if (methodFilter(methods[i])) {
67 continue;
68 }
69 methodLookupList.add(methods[i]);
70 }
71 final Map methodSequences = new HashMap();
72 final List wrapperMethods = new ArrayList();
73 boolean isClassAdvised = false;
74 for (Iterator i = methodLookupList.iterator(); i.hasNext();) {
75 CtMethod method = (CtMethod) i.next();
76
77
78
79 if (methodSequences.containsKey(method.getName())) {
80 int sequence = ((Integer) methodSequences.get(method.getName())).intValue();
81 methodSequences.remove(method.getName());
82 sequence++;
83 methodSequences.put(method.getName(), new Integer(sequence));
84 } else {
85 methodSequences.put(method.getName(), new Integer(1));
86 }
87 final int methodSequence = ((Integer) methodSequences.get(method.getName())).intValue();
88 CtMethod wrapperMethod = createEmptyWrapperMethod(ctClass, method, methodSequence);
89 if (wrapperMethod != null) {
90 isClassAdvised = true;
91 wrapperMethods.add(wrapperMethod);
92 }
93 }
94 if (isClassAdvised) {
95 context.markAsAdvised();
96
97
98 for (Iterator it2 = wrapperMethods.iterator(); it2.hasNext();) {
99 ctClass.addMethod((CtMethod) it2.next());
100 }
101 }
102 }
103 }
104
105 /***
106 * Creates an empty wrapper method to allow HotSwap without schema change
107 *
108 * @param ctClass the ClassGen
109 * @param originalMethod the current method
110 * @param methodSequence the method hash
111 * @return the wrapper method
112 */
113 private CtMethod createEmptyWrapperMethod(
114 final CtClass ctClass,
115 final CtMethod originalMethod,
116 final int methodSequence) throws NotFoundException, CannotCompileException {
117 String wrapperMethodName = TransformationUtil.getPrefixedOriginalMethodName(
118 originalMethod.getName(),
119 methodSequence,
120 ctClass.getName().replace('/', '.'));
121
122
123 if (JavassistHelper.hasMethod(ctClass, wrapperMethodName)) {
124 return null;
125 }
126
127
128 int accessFlags = originalMethod.getModifiers();
129 if ((accessFlags & Modifier.PROTECTED) == 0) {
130
131 accessFlags |= Modifier.PROTECTED;
132 }
133 if ((accessFlags & Modifier.PRIVATE) != 0) {
134
135 accessFlags &= ~Modifier.PRIVATE;
136 }
137 if ((accessFlags & Modifier.PUBLIC) != 0) {
138
139 accessFlags &= ~Modifier.PUBLIC;
140 }
141
142
143 StringBuffer body = new StringBuffer();
144 if (originalMethod.getReturnType() == CtClass.voidType) {
145
146
147 body.append("{}");
148 } else if (!originalMethod.getReturnType().isPrimitive()) {
149 body.append("{ return null;}");
150 } else {
151 body.append("{ return ");
152 body.append(JavassistHelper.getDefaultPrimitiveValue(originalMethod.getReturnType()));
153 body.append("; }");
154 }
155 CtMethod method = null;
156 if (Modifier.isStatic(originalMethod.getModifiers())) {
157 method = JavassistHelper.makeStatic(originalMethod.getReturnType(), wrapperMethodName, originalMethod
158 .getParameterTypes(), originalMethod.getExceptionTypes(), body.toString(), ctClass);
159 } else {
160 method = CtNewMethod.make(originalMethod.getReturnType(), wrapperMethodName, originalMethod
161 .getParameterTypes(), originalMethod.getExceptionTypes(), body.toString(), ctClass);
162 method.setModifiers(accessFlags);
163 }
164
165
166
167 JavassistHelper.setAnnotatedEmpty(method);
168 return method;
169 }
170
171 /***
172 * Filters the classes to be transformed. Takes only "prepare" declarations into account
173 *
174 * @param definition the definition
175 * @param cg the class to filter
176 * @return boolean true if the method should be filtered away
177 */
178 public static boolean classFilter(final SystemDefinition definition, final CtClass cg) {
179 if (cg.isInterface()) {
180 return true;
181 }
182 String className = cg.getName().replace('/', '.');
183 if (definition.inExcludePackage(className)) {
184 return true;
185 }
186 if (!definition.inIncludePackage(className)) {
187 return true;
188 }
189 if (definition.inPreparePackage(className)) {
190 return false;
191 }
192 return true;
193 }
194
195 /***
196 * Filters the methods to be transformed. Does not check execution pointcuts
197 *
198 * @param method the method to filter
199 * @return boolean
200 */
201 public static boolean methodFilter(final CtMethod method) {
202 if (Modifier.isAbstract(method.getModifiers())
203 || Modifier.isNative(method.getModifiers())
204 || method.getName().equals("<init>")
205 || method.getName().equals("<clinit>")
206 || method.getName().startsWith(TransformationConstants.ORIGINAL_METHOD_PREFIX)
207 || method.getName().equals(TransformationConstants.CLASS_LOOKUP_METHOD)) {
208 return true;
209 }
210 return false;
211 }
212 }