001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.joinpoint.management;
008:
009: import java.util.ArrayList;
010: import java.util.Collection;
011: import java.util.Iterator;
012: import java.util.List;
013: import java.util.Map;
014: import java.util.Set;
015: import java.util.WeakHashMap;
016:
017: import org.codehaus.aspectwerkz.aspect.AdviceInfo;
018: import org.codehaus.aspectwerkz.aspect.AdviceType;
019: import org.codehaus.aspectwerkz.definition.AdviceDefinition;
020: import org.codehaus.aspectwerkz.definition.AspectDefinition;
021: import org.codehaus.aspectwerkz.definition.SystemDefinition;
022: import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
023: import org.codehaus.aspectwerkz.definition.Virtual;
024: import org.codehaus.aspectwerkz.expression.ArgsIndexVisitor;
025: import org.codehaus.aspectwerkz.expression.ExpressionContext;
026: import org.codehaus.aspectwerkz.expression.ExpressionInfo;
027: import org.codehaus.aspectwerkz.expression.PointcutType;
028: import org.codehaus.aspectwerkz.joinpoint.ConstructorSignature;
029: import org.codehaus.aspectwerkz.joinpoint.EnclosingStaticJoinPoint;
030: import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
031: import org.codehaus.aspectwerkz.joinpoint.MethodSignature;
032: import org.codehaus.aspectwerkz.joinpoint.Signature;
033: import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
034: import org.codehaus.aspectwerkz.joinpoint.impl.EnclosingStaticJoinPointImpl;
035: import org.codehaus.aspectwerkz.reflect.ClassInfo;
036: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
037: import org.codehaus.aspectwerkz.reflect.MethodInfo;
038: import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
039: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
040: import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
041: import org.codehaus.aspectwerkz.transform.TransformationConstants;
042: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
043: import org.codehaus.aspectwerkz.transform.inlining.AspectModelManager;
044: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
045: import org.codehaus.aspectwerkz.transform.inlining.compiler.CompilationInfo;
046: import org.codehaus.aspectwerkz.transform.inlining.compiler.JoinPointFactory;
047: import org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel;
048: import org.codehaus.aspectwerkz.util.ContextClassLoader;
049: import org.codehaus.aspectwerkz.util.Strings;
050: import org.objectweb.asm.Type;
051:
052: /**
053: * Manages the join point compilation, loading and instantiation for the target classes.
054: * This implementation relies on the SystemDefinitionContainer.
055: *
056: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
057: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
058: * @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
059: */
060: public class JoinPointManager {
061: /**
062: * Ensures that the specific joinPoint class for the given target class and joinPoint info is generated. This call
063: * is added to the weaved class as a "clinit" block
064: *
065: * @param joinPointType
066: * @param callerClass
067: * @param callerMethodName
068: * @param callerMethodDesc
069: * @param callerMethodModifiers
070: * @param calleeClassName
071: * @param calleeMemberName
072: * @param calleeMemberDesc
073: * @param calleeMemberModifiers
074: * @param joinPointHash
075: * @param joinPointClassName
076: */
077: public static void loadJoinPoint(final int joinPointType,
078: final Class callerClass, final String callerMethodName,
079: final String callerMethodDesc,
080: final int callerMethodModifiers,
081: final String calleeClassName,
082: final String calleeMemberName,
083: final String calleeMemberDesc,
084: final int calleeMemberModifiers, final int joinPointHash,
085: final String joinPointClassName) {
086: Class calleeClass = null;
087: try {
088: if (calleeClassName != null) {
089: calleeClass = Class.forName(calleeClassName.replace(
090: '/', '.'), false, callerClass.getClassLoader());
091: }
092: } catch (ClassNotFoundException calleeNotFound) {
093: throw new RuntimeException("callee class ["
094: + calleeClassName
095: + "] can not be found in class loader ["
096: + callerClass.getClassLoader() + "]");
097: }
098:
099: // check if the JP is already loaded
100: // this can occurs if user packaged its JIT classes, or if we are using multiweaving
101: final ClassLoader classLoader = callerClass.getClassLoader();
102: boolean generateJoinPoint = false;
103: try {
104: if (calleeClass == null) {
105: throw new RuntimeException("callee class ["
106: + calleeClassName + "] is NULL");
107: }
108: ContextClassLoader.forName(classLoader, joinPointClassName
109: .replace('/', '.'));
110: } catch (ClassNotFoundException e) {
111: generateJoinPoint = true;
112: }
113: if (!generateJoinPoint) {
114: return;
115: }
116:
117: final CompiledJoinPoint compiledJoinPoint = compileJoinPoint(
118: joinPointType, callerClass, callerMethodName,
119: callerMethodDesc, callerMethodModifiers,
120: calleeClassName, calleeMemberName, calleeMemberDesc,
121: calleeMemberModifiers, joinPointHash,
122: joinPointClassName, calleeClass, classLoader);
123:
124: Class jpClass = JoinPointFactory.attachToClassLoader(
125: joinPointClassName, classLoader,
126: compiledJoinPoint.bytecode);
127: JoinPointFactory.addCompilationInfo(jpClass,
128: compiledJoinPoint.compilationInfo);
129: }
130:
131: /**
132: * Compile a new joinpoint
133: *
134: * @param joinPointType
135: * @param callerClass
136: * @param callerMethodName
137: * @param callerMethodDesc
138: * @param callerMethodModifiers
139: * @param calleeClassName
140: * @param calleeMemberName
141: * @param calleeMemberDesc
142: * @param calleeMemberModifiers
143: * @param joinPointHash
144: * @param joinPointClassName
145: * @param calleeClass
146: * @param loader the loader that hosts the definitions, and from where caller, callee and aspect are visible.
147: * At runtime it is exactly callerClass.getClassLoader() but in offline mode and genjp, it can happen to be
148: * different when weaved class also exists in the compilation classpath.
149: * @return
150: */
151: public static CompiledJoinPoint compileJoinPoint(
152: final int joinPointType, final Class callerClass,
153: final String callerMethodName,
154: final String callerMethodDesc,
155: final int callerMethodModifiers,
156: final String calleeClassName,
157: final String calleeMemberName,
158: final String calleeMemberDesc,
159: final int calleeMemberModifiers, final int joinPointHash,
160: final String joinPointClassName, final Class calleeClass,
161: final ClassLoader loader) {
162:
163: ClassInfo calleeClassInfo = JavaClassInfo
164: .getClassInfo(calleeClass);
165:
166: // create the callee info
167: final ReflectionInfo reflectionInfo;
168: final PointcutType pointcutType;
169: switch (joinPointType) {
170: case JoinPointType.STATIC_INITIALIZATION_INT:
171: reflectionInfo = calleeClassInfo.staticInitializer();
172: pointcutType = PointcutType.STATIC_INITIALIZATION;
173: break;
174: case JoinPointType.METHOD_EXECUTION_INT:
175: reflectionInfo = calleeClassInfo.getMethod(joinPointHash);
176: pointcutType = PointcutType.EXECUTION;
177: break;
178: case JoinPointType.METHOD_CALL_INT:
179: reflectionInfo = calleeClassInfo.getMethod(joinPointHash);
180: pointcutType = PointcutType.CALL;
181: break;
182: case JoinPointType.FIELD_GET_INT:
183: reflectionInfo = calleeClassInfo.getField(joinPointHash);
184: pointcutType = PointcutType.GET;
185: break;
186: case JoinPointType.FIELD_SET_INT:
187: reflectionInfo = calleeClassInfo.getField(joinPointHash);
188: pointcutType = PointcutType.SET;
189: break;
190: case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
191: reflectionInfo = calleeClassInfo
192: .getConstructor(joinPointHash);
193: pointcutType = PointcutType.EXECUTION;
194: break;
195: case JoinPointType.CONSTRUCTOR_CALL_INT:
196: reflectionInfo = calleeClassInfo
197: .getConstructor(joinPointHash);
198: pointcutType = PointcutType.CALL;
199: break;
200: case JoinPointType.HANDLER_INT:
201: reflectionInfo = calleeClassInfo;
202: pointcutType = PointcutType.HANDLER;
203: break;
204: default:
205: throw new RuntimeException("Joinpoint type not supported: "
206: + joinPointType);
207: }
208:
209: // create the caller info
210: final ClassInfo callerClassInfo = JavaClassInfo
211: .getClassInfo(callerClass);
212: final ReflectionInfo withinInfo;
213: if (TransformationConstants.CLINIT_METHOD_NAME
214: .equals(callerMethodName)) {
215: withinInfo = callerClassInfo.staticInitializer();
216: } else if (TransformationConstants.INIT_METHOD_NAME
217: .equals(callerMethodName)) {
218: withinInfo = callerClassInfo.getConstructor(AsmHelper
219: .calculateConstructorHash(callerMethodDesc));
220: } else {
221: withinInfo = callerClassInfo.getMethod(AsmHelper
222: .calculateMethodHash(callerMethodName,
223: callerMethodDesc));
224: }
225:
226: // get the compilation model
227: final ExpressionContext ctx = new ExpressionContext(
228: pointcutType, reflectionInfo, withinInfo);
229: final AdviceInfoContainer adviceContainer = getAdviceInfoContainerForJoinPoint(
230: ctx, loader);
231: final EmittedJoinPoint emittedJoinPoint = new EmittedJoinPoint(
232: joinPointType, callerClass.getName(), callerMethodName,
233: callerMethodDesc, callerMethodModifiers, calleeClass
234: .getName(), calleeMemberName, calleeMemberDesc,
235: calleeMemberModifiers, joinPointHash,
236: joinPointClassName, EmittedJoinPoint.NO_LINE_NUMBER);
237: final CompilationInfo.Model compilationModel = new CompilationInfo.Model(
238: emittedJoinPoint, adviceContainer, callerClassInfo);
239:
240: return new CompiledJoinPoint(compilationModel);
241: }
242:
243: /**
244: * A compiled joinpoint is tied to a compilation model at a given time
245: */
246: public static class CompiledJoinPoint {
247: public byte[] bytecode;
248: public CompilationInfo compilationInfo;
249:
250: public CompiledJoinPoint(CompilationInfo.Model model) {
251: bytecode = JoinPointFactory.compileJoinPoint(model);
252: compilationInfo = new CompilationInfo(model);
253: }
254: }
255:
256: /**
257: * Retrieves the advice info wrapped up in a struct.
258: *
259: * @param expressionContext
260: * @param loader
261: * @return the advice info
262: */
263: public static AdviceInfoContainer getAdviceInfoContainerForJoinPoint(
264: final ExpressionContext expressionContext,
265: final ClassLoader loader) {
266: final List beforeAdvices = new ArrayList();
267: final List aroundAdvices = new ArrayList();
268: final List afterFinallyAdvices = new ArrayList();
269: final List afterReturningAdvices = new ArrayList();
270: final List afterThrowingAdvices = new ArrayList();
271:
272: final Set systemDefinitions = SystemDefinitionContainer
273: .getDefinitionsFor(loader);
274:
275: for (Iterator iterator = systemDefinitions.iterator(); iterator
276: .hasNext();) {
277: SystemDefinition systemDefinition = (SystemDefinition) iterator
278: .next();
279: Collection aspects = systemDefinition
280: .getAspectDefinitions();
281: for (Iterator iterator1 = aspects.iterator(); iterator1
282: .hasNext();) {
283: AspectDefinition aspectDefinition = (AspectDefinition) iterator1
284: .next();
285: if (aspectDefinition.getName().equals(
286: Virtual.class.getName())) {
287: continue;
288: }
289:
290: //TODO - do we care about non bounded pointcut ?
291: for (Iterator iterator2 = aspectDefinition
292: .getAdviceDefinitions().iterator(); iterator2
293: .hasNext();) {
294: AdviceDefinition adviceDefinition = (AdviceDefinition) iterator2
295: .next();
296: final ExpressionInfo expressionInfo = adviceDefinition
297: .getExpressionInfo();
298: if (expressionInfo == null) {
299: continue;
300: }
301: if (expressionInfo.getExpression().match(
302: expressionContext)) {
303: // compute the target method to advice method arguments map, and grab information about this
304: // and target bindings
305: expressionContext.resetRuntimeState();
306: ArgsIndexVisitor
307: .updateContextForRuntimeInformation(
308: expressionInfo,
309: expressionContext, loader);
310: // Note that the expressionContext dynamic information updated here should only be used
311: // in the scope of this code block, since at the next iteration, the data will be
312: // updated for another advice binding
313: // [hence see setMethodArgumentIndexes below]
314:
315: // create a lightweight representation of the bounded advices to pass to the compiler
316: final MethodInfo adviceMethodInfo = adviceDefinition
317: .getMethodInfo();
318: final AdviceInfo adviceInfo = new AdviceInfo(
319: aspectDefinition.getQualifiedName(),
320: aspectDefinition.getClassName(),
321: aspectDefinition.getDeploymentModel(),
322: adviceMethodInfo.getName(),
323: AsmHelper
324: .getMethodDescriptor(adviceMethodInfo),
325: AsmHelper
326: .getArgumentTypes(adviceMethodInfo),
327: adviceDefinition.getType(),
328: adviceDefinition
329: .getSpecialArgumentType(),
330: adviceDefinition.getName(),
331: expressionContext.m_targetWithRuntimeCheck,
332: expressionInfo, expressionContext,
333: adviceDefinition);
334:
335: setMethodArgumentIndexes(expressionInfo,
336: expressionContext, adviceInfo, loader);
337:
338: if (AdviceType.BEFORE.equals(adviceDefinition
339: .getType())) {
340: beforeAdvices.add(adviceInfo);
341: } else if (AdviceType.AROUND
342: .equals(adviceDefinition.getType())) {
343: aroundAdvices.add(adviceInfo);
344: } else if (AdviceType.AFTER_FINALLY
345: .equals(adviceDefinition.getType())) {
346: afterFinallyAdvices.add(adviceInfo);
347: } else if (AdviceType.AFTER_RETURNING
348: .equals(adviceDefinition.getType())) {
349: afterReturningAdvices.add(adviceInfo);
350: } else if (AdviceType.AFTER_THROWING
351: .equals(adviceDefinition.getType())) {
352: afterThrowingAdvices.add(adviceInfo);
353: } else if (AdviceType.AFTER
354: .equals(adviceDefinition.getType())) {
355: afterFinallyAdvices.add(adviceInfo);//special case for "after only"
356: }
357: }
358: }
359: }
360: }
361:
362: final AdviceInfoContainer adviceInfoContainer = new AdviceInfoContainer(
363: aroundAdvices, beforeAdvices, afterFinallyAdvices,
364: afterReturningAdvices, afterThrowingAdvices);
365: return adviceInfoContainer;
366: }
367:
368: /**
369: * Get the parameter names from a "method declaration" signature like pc(type a, type2 b) => 0:a, 1:b
370: *
371: * @param adviceName
372: * @return the parameter names
373: */
374: public static String[] getParameterNames(final String adviceName) {
375: int paren = adviceName.indexOf('(');
376: List paramNames = new ArrayList();
377: if (paren > 0) {
378: String params = adviceName.substring(paren + 1,
379: adviceName.lastIndexOf(')')).trim();
380: String[] javaParameters = Strings.splitString(params, ",");
381: for (int i = 0; i < javaParameters.length; i++) {
382: String javaParameter = Strings.replaceSubString(
383: javaParameters[i], " ", " ").trim();
384: String[] paramInfo = Strings.splitString(javaParameter,
385: " ");
386: // handles XML def where name is optional
387: if (paramInfo.length == 2) {
388: paramNames.add(paramInfo[1]);
389: } else {
390: paramNames.add("anonymous_" + i);
391: }
392: }
393: }
394: String[] paramNamesArray = new String[paramNames.size()];
395: int index = 0;
396: for (Iterator it = paramNames.iterator(); it.hasNext(); index++) {
397: paramNamesArray[index] = (String) it.next();
398: }
399: return paramNamesArray;
400: }
401:
402: /**
403: * Sets the advice argument indexes map.
404: * <p/>
405: * Each advice arg is mapped to wether a system entity like StaticJoinPoint, Rtti, this, target etc thru a specific index
406: * (see AdviceInfo), or to one of the advised member arguments (thru args(..) binding).
407: *
408: * @param expressionInfo
409: * @param ctx
410: * @param adviceInfo
411: * @param loader
412: */
413: private static void setMethodArgumentIndexes(
414: final ExpressionInfo expressionInfo,
415: final ExpressionContext ctx, final AdviceInfo adviceInfo,
416: final ClassLoader loader) {
417: // grab the parameters names
418: String[] adviceArgNames = getParameterNames(adviceInfo
419: .getName());
420:
421: // map them from the ctx info
422: int[] adviceToTargetArgs = new int[adviceInfo
423: .getMethodParameterTypes().length];
424: for (int k = 0; k < adviceArgNames.length; k++) {
425: String adviceArgName = adviceArgNames[k];
426: int exprArgIndex = expressionInfo
427: .getArgumentIndex(adviceArgName);
428: if (exprArgIndex >= 0
429: && ctx.m_exprIndexToTargetIndex
430: .containsKey(adviceArgName)) {
431: adviceToTargetArgs[k] = ctx.m_exprIndexToTargetIndex
432: .get(adviceArgName);
433: } else {
434: // does not appears to be an argument of the advised target
435: // It can be StaticJP / JP / This binding / Target binding
436: final Type type = adviceInfo.getMethodParameterTypes()[k];
437: if (isJoinPoint(type)) {
438: adviceToTargetArgs[k] = AdviceInfo.JOINPOINT_ARG;
439: } else if (isStaticJoinPoint(type)) {
440: adviceToTargetArgs[k] = AdviceInfo.STATIC_JOINPOINT_ARG;
441: } else if (isTarget(adviceArgName, ctx)) {
442: adviceToTargetArgs[k] = AdviceInfo.TARGET_ARG;
443: } else if (isThis(adviceArgName, ctx)) {
444: adviceToTargetArgs[k] = AdviceInfo.THIS_ARG;
445: } else if (isSpecialArgument(adviceArgName,
446: expressionInfo)) {
447: adviceToTargetArgs[k] = AdviceInfo.SPECIAL_ARGUMENT;
448: } else if (isCustomJointPoint(type, loader)) {
449: adviceToTargetArgs[k] = AdviceInfo.CUSTOM_JOIN_POINT_ARG;
450: } else {
451: throw new Error(
452: "Unbound advice parameter at index " + k
453: + " in "
454: + adviceInfo.getMethodName()
455: + adviceInfo.getMethodSignature()
456: + " named " + adviceArgName);
457: }
458: }
459: }
460:
461: // support for old style advices in XML whose name does not contain the call signature
462: if (adviceArgNames.length == 0) {
463: AspectDefinition aspectDef = adviceInfo
464: .getAdviceDefinition().getAspectDefinition();
465: Type[] adviceArgTypes = adviceInfo
466: .getMethodParameterTypes();
467: for (int i = 0; i < adviceArgTypes.length; i++) {
468:
469: if (aspectDef.isAspectWerkzAspect()) {
470: if (isJoinPoint(adviceArgTypes[i])) {
471: adviceToTargetArgs[i] = AdviceInfo.JOINPOINT_ARG;
472: } else if (isStaticJoinPoint(adviceArgTypes[i])) {
473: adviceToTargetArgs[i] = AdviceInfo.STATIC_JOINPOINT_ARG;
474: } else {
475: throw new Error(
476: "Unbound unnamed advice parameter at index "
477: + i
478: + " in "
479: + adviceInfo
480: .getMethodSignature());
481: }
482: } else {
483: final AspectModel aspectModel = AspectModelManager
484: .getModelFor(aspectDef.getAspectModel());
485: final String super ClassName = aspectModel
486: .getAroundClosureClassInfo()
487: .getSuperClassName();
488: final String[] interfaces = aspectModel
489: .getAroundClosureClassInfo()
490: .getInterfaceNames();
491: final String[] classNames = new String[interfaces.length + 1];
492: classNames[0] = super ClassName;
493: for (int j = 1; j < interfaces.length + 1; j++) {
494: classNames[j] = interfaces[j - 1];
495: }
496:
497: final Type argType = adviceArgTypes[i];
498: if (isValidAroundClosureType(argType, classNames)) {
499: adviceToTargetArgs[i] = AdviceInfo.VALID_NON_AW_AROUND_CLOSURE_TYPE;
500: } else if (isSpecialArgumentType(argType,
501: adviceInfo)) {
502: adviceToTargetArgs[i] = AdviceInfo.SPECIAL_ARGUMENT;
503: } else {
504: }
505: }
506: }
507: }
508:
509: adviceInfo.setMethodToArgIndexes(adviceToTargetArgs);
510: }
511:
512: private static boolean isSpecialArgumentType(final Type argType,
513: final AdviceInfo adviceInfo) {
514: final String specialArgumentTypeDesc = adviceInfo
515: .getSpecialArgumentTypeDesc();
516: return specialArgumentTypeDesc != null
517: && specialArgumentTypeDesc.equals(argType
518: .getDescriptor());
519: }
520:
521: private static boolean isValidAroundClosureType(final Type argType,
522: final String[] closureTypeNames) {
523: for (int i = 0; i < closureTypeNames.length; i++) {
524: final String closureTypeName = closureTypeNames[i];
525: if (closureTypeName != null
526: && closureTypeName
527: .equals(argType.getInternalName())) {
528: return true;
529: }
530: }
531: return false;
532: }
533:
534: private static boolean isJoinPoint(final Type type) {
535: return Type.getType(JoinPoint.class).getDescriptor().equals(
536: type.getDescriptor());
537: }
538:
539: private static boolean isStaticJoinPoint(final Type type) {
540: return Type.getType(StaticJoinPoint.class).getDescriptor()
541: .equals(type.getDescriptor());
542: }
543:
544: private static boolean isTarget(final String adviceArgName,
545: final ExpressionContext ctx) {
546: return adviceArgName.equals(ctx.m_targetBoundedName);
547: }
548:
549: private static boolean isThis(final String adviceArgName,
550: final ExpressionContext ctx) {
551: return adviceArgName.equals(ctx.m_this BoundedName);
552: }
553:
554: private static boolean isSpecialArgument(
555: final String adviceArgName,
556: final ExpressionInfo expressionInfo) {
557: return adviceArgName.equals(expressionInfo
558: .getSpecialArgumentName());
559: }
560:
561: private static boolean isCustomJointPoint(final Type type,
562: final ClassLoader loader) {
563: ClassInfo classInfo = AsmClassInfo.getClassInfo(type
564: .getClassName(), loader);
565: return ClassInfoHelper.implementsInterface(classInfo,
566: ExpressionInfo.JOINPOINT_CLASS_NAME)
567: || ClassInfoHelper.implementsInterface(classInfo,
568: ExpressionInfo.STATIC_JOINPOINT_CLASS_NAME);
569: }
570: }
|