/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.intercept;
import java.util.ArrayList;
import java.util.List;
import java.lang.reflect.Field;
import gnu.trove.TIntObjectHashMap;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
import org.codehaus.aspectwerkz.expression.PointcutType;
import org.codehaus.aspectwerkz.expression.ExpressionContext;
import org.codehaus.aspectwerkz.expression.ExpressionInfo;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
/**
* Implementation of the <code>Advisable</code> mixin.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public class AdvisableImpl implements Advisable {
private static final String EXPRESSION_NAMESPACE = "___AW_ADVISABLE_AW___";
public static final ClassInfo CLASS_INFO;
public static final String ADD_ADVICE_METHOD_NAME = "aw_addAdvice";
public static final String ADD_ADVICE_METHOD_DESC = "(Ljava/lang/String;Lorg/codehaus/aspectwerkz/intercept/Advice;)V";
public static final String REMOVE_ADVICE_METHOD_NAME = "aw_removeAdvice";
public static final String REMOVE_ADVICE_METHOD_DESC = "(Ljava/lang/String;Ljava/lang/Class;)V";
public static final AroundAdvice[] EMPTY_AROUND_ADVICE_ARRAY = new AroundAdvice[0];
public static final BeforeAdvice[] EMPTY_BEFORE_ADVICE_ARRAY = new BeforeAdvice[0];
public static final AfterAdvice[] EMPTY_AFTER_ADVICE_ARRAY = new AfterAdvice[0];
public static final AfterReturningAdvice[] EMPTY_AFTER_RETURNING_ADVICE_ARRAY = new AfterReturningAdvice[0];
public static final AfterThrowingAdvice[] EMPTY_AFTER_THROWING_ADVICE_ARRAY = new AfterThrowingAdvice[0];
static {
final Class clazz = AdvisableImpl.class;
try {
CLASS_INFO = AsmClassInfo.getClassInfo(clazz.getName(), clazz.getClassLoader());
} catch (Exception e) {
throw new Error("could not create class info for [" + clazz.getName() + ']');
}
}
private final Advisable m_targetInstance;
private final TIntObjectHashMap m_emittedJoinPoints;
private final TIntObjectHashMap m_aroundAdvice = new TIntObjectHashMap();
private final TIntObjectHashMap m_beforeAdvice = new TIntObjectHashMap();
private final TIntObjectHashMap m_afterAdvice = new TIntObjectHashMap();
private final TIntObjectHashMap m_afterReturningAdvice = new TIntObjectHashMap();
private final TIntObjectHashMap m_afterThrowingAdvice = new TIntObjectHashMap();
/**
* Creates a new mixin impl.
*
* @param targetInstance the target for this mixin instance (perInstance deployed)
*/
public AdvisableImpl(final Object targetInstance) {
if (!(targetInstance instanceof Advisable)) {
throw new RuntimeException(
"advisable mixin applied to target class that does not implement the Advisable interface"
);
}
m_targetInstance = (Advisable) targetInstance;
try {
Field f = targetInstance.getClass().getDeclaredField("aw$emittedJoinPoints");
f.setAccessible(true);
m_emittedJoinPoints = (TIntObjectHashMap) f.get(null);
} catch (Exception e) {
throw new RuntimeException(
"advisable mixin applied to target class cannot access reflective information: " + e.toString()
);
}
}
/**
* @param pointcut
* @param advice
*/
public void aw_addAdvice(final String pointcut, final Advice advice) {
addAdvice(pointcut, advice);
}
/**
* @param pointcut
* @param adviceClass
*/
public void aw_removeAdvice(final String pointcut, final Class adviceClass) {
removeAdvice(pointcut, adviceClass);
}
/**
* @param joinPointIndex
* @return
*/
public AroundAdvice[] aw$getAroundAdvice(final int joinPointIndex) {
Object advice = m_aroundAdvice.get(joinPointIndex);
if (advice == null) {
return EMPTY_AROUND_ADVICE_ARRAY;
} else {
return (AroundAdvice[]) advice;
}
}
/**
* @param joinPointIndex
* @return
*/
public BeforeAdvice[] aw$getBeforeAdvice(final int joinPointIndex) {
Object advice = m_beforeAdvice.get(joinPointIndex);
if (advice == null) {
return EMPTY_BEFORE_ADVICE_ARRAY;
} else {
return (BeforeAdvice[]) advice;
}
}
/**
* @param joinPointIndex
* @return
*/
public AfterAdvice[] aw$getAfterAdvice(final int joinPointIndex) {
Object advice = m_afterAdvice.get(joinPointIndex);
if (advice == null) {
return EMPTY_AFTER_ADVICE_ARRAY;
} else {
return (AfterAdvice[]) advice;
}
}
/**
* @param joinPointIndex
* @return
*/
public AfterReturningAdvice[] aw$getAfterReturningAdvice(final int joinPointIndex) {
Object advice = m_afterReturningAdvice.get(joinPointIndex);
if (advice == null) {
return EMPTY_AFTER_RETURNING_ADVICE_ARRAY;
} else {
return (AfterReturningAdvice[]) advice;
}
}
/**
* @param joinPointIndex
* @return
*/
public AfterThrowingAdvice[] aw$getAfterThrowingAdvice(final int joinPointIndex) {
Object advice = m_afterThrowingAdvice.get(joinPointIndex);
if (advice == null) {
return EMPTY_AFTER_THROWING_ADVICE_ARRAY;
} else {
return (AfterThrowingAdvice[]) advice;
}
}
/**
* @param pointcut
* @param advice
*/
private void addAdvice(final String pointcut,
final Advice advice) {
ExpressionInfo expressionInfo = new ExpressionInfo(pointcut, EXPRESSION_NAMESPACE);
Object[] emittedJoinPoints = m_emittedJoinPoints.getValues();
for (int i = 0; i < emittedJoinPoints.length; i++) {
EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) emittedJoinPoints[i];
if (match(expressionInfo, PointcutType.EXECUTION, emittedJoinPoint)
|| match(expressionInfo, PointcutType.CALL, emittedJoinPoint)
|| match(expressionInfo, PointcutType.HANDLER, emittedJoinPoint)
|| match(expressionInfo, PointcutType.GET, emittedJoinPoint)
|| match(expressionInfo, PointcutType.SET, emittedJoinPoint)
//note: STATIC INIT is useless since the class is already loaded to manipulate the instance
) {
int hash = emittedJoinPoint.getJoinPointClassName().hashCode();
addAroundAdvice(advice, hash);
addBeforeAdvice(advice, hash);
addAfterAdvice(advice, hash);
addAfterReturningAdvice(advice, hash);
addAfterThrowingAdvice(advice, hash);
}
}
}
/**
* @param pointcut
* @param adviceClass
*/
private void removeAdvice(final String pointcut,
final Class adviceClass) {
ExpressionInfo expressionInfo = new ExpressionInfo(pointcut, EXPRESSION_NAMESPACE);
Object[] emittedJoinPoints = m_emittedJoinPoints.getValues();
for (int i = 0; i < emittedJoinPoints.length; i++) {
EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) emittedJoinPoints[i];
if (match(expressionInfo, PointcutType.EXECUTION, emittedJoinPoint)
|| match(expressionInfo, PointcutType.CALL, emittedJoinPoint)
|| match(expressionInfo, PointcutType.HANDLER, emittedJoinPoint)
|| match(expressionInfo, PointcutType.GET, emittedJoinPoint)
|| match(expressionInfo, PointcutType.SET, emittedJoinPoint)
//note: STATIC INIT is useless since the class is already loaded to manipulate the instance
) {
int hash = emittedJoinPoint.getJoinPointClassName().hashCode();
removeAroundAdvice(adviceClass, hash);
removeBeforeAdvice(adviceClass, hash);
removeAfterAdvice(adviceClass, hash);
removeAfterReturningAdvice(adviceClass, hash);
removeAfterThrowingAdvice(adviceClass, hash);
}
}
}
/**
* @param advice
* @param joinPointHash
*/
private void addAroundAdvice(final Advice advice, int joinPointHash) {
if (advice instanceof AroundAdvice) {
AroundAdvice aroundAdvice = (AroundAdvice) advice;
AroundAdvice[] advices;
AroundAdvice[] olds = aw$getAroundAdvice(joinPointHash);
if (olds != null) {
advices = new AroundAdvice[olds.length + 1];
System.arraycopy(olds, 0, advices, 0, olds.length);
advices[advices.length - 1] = aroundAdvice;
} else {
advices = new AroundAdvice[]{aroundAdvice};
}
m_aroundAdvice.put(joinPointHash, advices);
}
}
/**
* @param advice
* @param joinPointHash
*/
private void addBeforeAdvice(final Advice advice, int joinPointHash) {
if (advice instanceof BeforeAdvice) {
BeforeAdvice beforeAdvice = (BeforeAdvice) advice;
BeforeAdvice[] advices;
BeforeAdvice[] olds = aw$getBeforeAdvice(joinPointHash);
if (olds != null) {
advices = new BeforeAdvice[olds.length + 1];
System.arraycopy(olds, 0, advices, 0, olds.length);
advices[advices.length - 1] = beforeAdvice;
} else {
advices = new BeforeAdvice[]{beforeAdvice};
}
m_beforeAdvice.put(joinPointHash, advices);
}
}
/**
* @param advice
* @param joinPointHash
*/
private void addAfterAdvice(final Advice advice, int joinPointHash) {
if (advice instanceof AfterAdvice) {
AfterAdvice afterFinallyAdvice = (AfterAdvice) advice;
AfterAdvice[] advices;
AfterAdvice[] olds = aw$getAfterAdvice(joinPointHash);
if (olds != null) {
advices = new AfterAdvice[olds.length + 1];
System.arraycopy(olds, 0, advices, 0, olds.length);
advices[advices.length - 1] = afterFinallyAdvice;
} else {
advices = new AfterAdvice[]{afterFinallyAdvice};
}
m_afterAdvice.put(joinPointHash, advices);
}
}
/**
* @param advice
* @param joinPointHash
*/
private void addAfterReturningAdvice(final Advice advice, int joinPointHash) {
if (advice instanceof AfterReturningAdvice) {
AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
AfterReturningAdvice[] advices;
AfterReturningAdvice[] olds = aw$getAfterReturningAdvice(joinPointHash);
if (olds != null) {
advices = new AfterReturningAdvice[olds.length + 1];
System.arraycopy(olds, 0, advices, 0, olds.length);
advices[advices.length - 1] = afterReturningAdvice;
} else {
advices = new AfterReturningAdvice[]{afterReturningAdvice};
}
m_afterReturningAdvice.put(joinPointHash, advices);
}
}
/**
* @param advice
* @param joinPointHash
*/
private void addAfterThrowingAdvice(final Advice advice, int joinPointHash) {
if (advice instanceof AfterThrowingAdvice) {
AfterThrowingAdvice afterThrowingAdvice = (AfterThrowingAdvice) advice;
AfterThrowingAdvice[] advices;
AfterThrowingAdvice[] olds = aw$getAfterThrowingAdvice(joinPointHash);
if (olds != null) {
advices = new AfterThrowingAdvice[olds.length + 1];
System.arraycopy(olds, 0, advices, 0, olds.length);
advices[advices.length - 1] = afterThrowingAdvice;
} else {
advices = new AfterThrowingAdvice[]{afterThrowingAdvice};
}
m_afterThrowingAdvice.put(joinPointHash, advices);
}
}
/**
* @param adviceClass
* @param joinPointHash
*/
private void removeAroundAdvice(final Class adviceClass, int joinPointHash) {
if (isAroundAdvice(adviceClass)) {
AroundAdvice[] oldArray = aw$getAroundAdvice(joinPointHash);
if (oldArray.length == 0) {
} else if (oldArray.length == 1) {
m_aroundAdvice.put(joinPointHash, EMPTY_AROUND_ADVICE_ARRAY);
} else {
List newArrayList = new ArrayList();
for (int i = 0; i < oldArray.length; i++) {
AroundAdvice aroundAdvice = oldArray[i];
if (!aroundAdvice.getClass().equals(adviceClass)) {
newArrayList.add(aroundAdvice);
}
}
m_aroundAdvice.put(
joinPointHash,
(AroundAdvice[]) newArrayList.toArray(new AroundAdvice[newArrayList.size()])
);
}
}
}
/**
* @param adviceClass
* @param joinPointHash
*/
private void removeBeforeAdvice(final Class adviceClass, int joinPointHash) {
if (isBeforeAdvice(adviceClass)) {
BeforeAdvice[] oldArray = aw$getBeforeAdvice(joinPointHash);
if (oldArray.length == 0) {
} else if (oldArray.length == 1) {
m_beforeAdvice.put(joinPointHash, EMPTY_BEFORE_ADVICE_ARRAY);
} else {
List newArrayList = new ArrayList();
for (int i = 0; i < oldArray.length; i++) {
BeforeAdvice beforeAdvice = oldArray[i];
if (!beforeAdvice.getClass().equals(adviceClass)) {
newArrayList.add(beforeAdvice);
}
}
m_beforeAdvice.put(
joinPointHash,
(BeforeAdvice[]) newArrayList.toArray(new BeforeAdvice[newArrayList.size()])
);
}
}
}
/**
* @param adviceClass
* @param joinPointHash
*/
private void removeAfterAdvice(final Class adviceClass, int joinPointHash) {
if (isAfterAdvice(adviceClass)) {
AfterAdvice[] oldArray = aw$getAfterAdvice(joinPointHash);
if (oldArray.length == 0) {
} else if (oldArray.length == 1) {
m_afterAdvice.put(joinPointHash, EMPTY_AFTER_ADVICE_ARRAY);
} else {
List newArrayList = new ArrayList();
for (int i = 0; i < oldArray.length; i++) {
AfterAdvice afterAdvice = oldArray[i];
if (!afterAdvice.getClass().equals(adviceClass)) {
newArrayList.add(afterAdvice);
}
}
m_afterAdvice.put(
joinPointHash,
(AfterAdvice[]) newArrayList.toArray(new AfterAdvice[newArrayList.size()])
);
}
}
}
/**
* @param adviceClass
* @param joinPointHash
*/
private void removeAfterReturningAdvice(final Class adviceClass, int joinPointHash) {
if (isAfterReturningAdvice(adviceClass)) {
AfterReturningAdvice[] oldArray = aw$getAfterReturningAdvice(joinPointHash);
if (oldArray.length == 0) {
} else if (oldArray.length == 1) {
m_afterReturningAdvice.put(joinPointHash, EMPTY_AFTER_RETURNING_ADVICE_ARRAY);
} else {
List newArrayList = new ArrayList();
for (int i = 0; i < oldArray.length; i++) {
AfterReturningAdvice afterReturningAdvice = oldArray[i];
if (!afterReturningAdvice.getClass().equals(adviceClass)) {
newArrayList.add(afterReturningAdvice);
}
}
m_afterReturningAdvice.put(
joinPointHash,
(AfterReturningAdvice[]) newArrayList.toArray(new AfterReturningAdvice[newArrayList.size()])
);
}
}
}
/**
* @param adviceClass
* @param joinPointHash
*/
private void removeAfterThrowingAdvice(final Class adviceClass, int joinPointHash) {
if (isAfterThrowingAdvice(adviceClass)) {
AfterThrowingAdvice[] oldArray = aw$getAfterThrowingAdvice(joinPointHash);
if (oldArray.length == 0) {
} else if (oldArray.length == 1) {
m_afterThrowingAdvice.put(joinPointHash, EMPTY_AFTER_THROWING_ADVICE_ARRAY);
} else {
List newArrayList = new ArrayList();
for (int i = 0; i < oldArray.length; i++) {
AfterThrowingAdvice advice = oldArray[i];
if (!advice.getClass().equals(adviceClass)) {
newArrayList.add(advice);
}
}
m_afterThrowingAdvice.put(
joinPointHash,
(AfterThrowingAdvice[]) newArrayList.toArray(new AfterThrowingAdvice[newArrayList.size()])
);
}
}
}
private boolean isAroundAdvice(final Class adviceClass) {
if (adviceClass == AroundAdvice.class) {
return true;
}
Class[] interfaces = adviceClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
if (anInterface == AroundAdvice.class) {
return true;
}
}
return false;
}
private boolean isBeforeAdvice(final Class adviceClass) {
if (adviceClass == BeforeAdvice.class) {
return true;
}
Class[] interfaces = adviceClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
if (anInterface == BeforeAdvice.class) {
return true;
}
}
return false;
}
private boolean isAfterAdvice(final Class adviceClass) {
if (adviceClass == AfterAdvice.class) {
return true;
}
Class[] interfaces = adviceClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
if (anInterface == AfterAdvice.class) {
return true;
}
}
return false;
}
private boolean isAfterReturningAdvice(final Class adviceClass) {
if (adviceClass == AfterReturningAdvice.class) {
return true;
}
Class[] interfaces = adviceClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
if (anInterface == AfterReturningAdvice.class) {
return true;
}
}
return false;
}
private boolean isAfterThrowingAdvice(final Class adviceClass) {
if (adviceClass == AfterThrowingAdvice.class) {
return true;
}
Class[] interfaces = adviceClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
if (anInterface == AfterThrowingAdvice.class) {
return true;
}
}
return false;
}
/**
* Match the given expression for the given pointcut type against the given emittedJoinPoint
*
* @param expression
* @param pointcutType
* @param emittedJoinPoint
* @return
*/
private boolean match(ExpressionInfo expression, PointcutType pointcutType, EmittedJoinPoint emittedJoinPoint) {
ClassInfo callerClassInfo = JavaClassInfo.getClassInfo(m_targetInstance.getClass());
ClassInfo calleeClassInfo = AsmClassInfo.getClassInfo(emittedJoinPoint.getCalleeClassName(), m_targetInstance.getClass().getClassLoader());
// early match
if (!expression.getAdvisedClassFilterExpression().match(new ExpressionContext(pointcutType, calleeClassInfo, callerClassInfo))) {
return false;
}
// create the callee info
final ReflectionInfo reflectionInfo;
final PointcutType joinPointType;
switch (emittedJoinPoint.getJoinPointType()) {
case JoinPointType.STATIC_INITIALIZATION_INT:
reflectionInfo = calleeClassInfo.staticInitializer();
joinPointType = PointcutType.STATIC_INITIALIZATION;
break;
case JoinPointType.METHOD_EXECUTION_INT:
reflectionInfo = calleeClassInfo.getMethod(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.EXECUTION;
break;
case JoinPointType.METHOD_CALL_INT:
reflectionInfo = calleeClassInfo.getMethod(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.CALL;
break;
case JoinPointType.FIELD_GET_INT:
reflectionInfo = calleeClassInfo.getField(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.GET;
break;
case JoinPointType.FIELD_SET_INT:
reflectionInfo = calleeClassInfo.getField(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.SET;
break;
case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
reflectionInfo = calleeClassInfo.getConstructor(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.EXECUTION;
break;
case JoinPointType.CONSTRUCTOR_CALL_INT:
reflectionInfo = calleeClassInfo.getConstructor(emittedJoinPoint.getJoinPointHash());
joinPointType = PointcutType.CALL;
break;
case JoinPointType.HANDLER_INT:
reflectionInfo = calleeClassInfo;
joinPointType = PointcutType.HANDLER;
break;
default:
throw new RuntimeException("Joinpoint type not supported: " + emittedJoinPoint.getJoinPointType());
}
// create the caller info
final ReflectionInfo withinInfo;
if (TransformationConstants.CLINIT_METHOD_NAME.equals(emittedJoinPoint.getCallerMethodName())) {
withinInfo = callerClassInfo.staticInitializer();
} else if (TransformationConstants.INIT_METHOD_NAME.equals(emittedJoinPoint.getCallerMethodName())) {
withinInfo = callerClassInfo.getConstructor(AsmHelper.calculateConstructorHash(
emittedJoinPoint.getCallerMethodDesc()
));
} else {
withinInfo =
callerClassInfo.getMethod(AsmHelper.calculateMethodHash(emittedJoinPoint.getCallerMethodName(),
emittedJoinPoint.getCallerMethodDesc())
);
}
// check pointcutType vs joinPointType
if (pointcutType != PointcutType.WITHIN && pointcutType != joinPointType) {
return false;
}
return expression.getExpression().match(new ExpressionContext(pointcutType, reflectionInfo, withinInfo));
}
}