/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.aop;
import gnu.trove.TLongObjectHashMap;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jboss.aop.advice.AdviceBinding;
import org.jboss.aop.advice.AspectDefinition;
import org.jboss.aop.advice.ClassifiedBindingAndPointcutCollection;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.instrument.ConstructorExecutionTransformer;
import org.jboss.aop.instrument.FieldAccessTransformer;
import org.jboss.aop.instrument.MethodExecutionTransformer;
import org.jboss.aop.introduction.InterfaceIntroduction;
import org.jboss.aop.joinpoint.ConstructorCalledByConstructorInvocation;
import org.jboss.aop.joinpoint.ConstructorCalledByConstructorJoinpoint;
import org.jboss.aop.joinpoint.ConstructorCalledByMethodInvocation;
import org.jboss.aop.joinpoint.ConstructorCalledByMethodJoinpoint;
import org.jboss.aop.joinpoint.ConstructorInvocation;
import org.jboss.aop.joinpoint.FieldJoinpoint;
import org.jboss.aop.joinpoint.FieldReadInvocation;
import org.jboss.aop.joinpoint.FieldWriteInvocation;
import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodCalledByConstructorInvocation;
import org.jboss.aop.joinpoint.MethodCalledByConstructorJoinpoint;
import org.jboss.aop.joinpoint.MethodCalledByMethodInvocation;
import org.jboss.aop.joinpoint.MethodCalledByMethodJoinpoint;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.aop.metadata.ClassMetaDataBinding;
import org.jboss.aop.metadata.ClassMetaDataLoader;
import org.jboss.aop.util.Advisable;
import org.jboss.aop.util.BindingClassifier;
import org.jboss.aop.util.ConstructorComparator;
import org.jboss.aop.util.FieldComparator;
import org.jboss.aop.util.MethodHashing;
import org.jboss.aop.util.ReflectUtils;
import org.jboss.aop.util.UnmodifiableEmptyCollections;
import org.jboss.aop.util.logging.AOPLogger;
import org.jboss.util.NotImplementedException;
/**
* Advises a class and provides access to the class's aspect chain.
* Each advisable class has an associated <code>Advisor</code> instance.
* References methods using <code>int</code> IDs rather than the actual
* instances for
* optimal performance. Provides ability to invoke methods on an advised
* object without advice (see
* <code>Advisor.invokeWithoutAdvisement()</code>).
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @version $Revision: 81450 $
*/
public class ClassAdvisor extends Advisor
{
private static final AOPLogger logger = AOPLogger.getLogger(ClassAdvisor.class);
/**
* Suffix added to unadvised methods.
*/
public static final String NOT_TRANSFORMABLE_SUFFIX = "$aop";
//Common sense suggests that this should be lazily initialised for generated advisors, profiling shows that is a major performance hit...
protected TLongObjectHashMap unadvisedMethods = new TLongObjectHashMap();
//Information about method by method caller pointcuts, lazy initialized when needed
private volatile MethodByMethodData methodByMethodData;
//Information about con by method caller pointcuts, lazy initialized when needed
private volatile ConByMethodData conByMethodData;
// caller pointcut support for constructors calling methods
protected HashMap<String, TLongObjectHashMap>[] methodCalledByConBindings; // TLongObjectHashMap contains Objects of type ArrayList<AdviceBinding>
protected HashMap<String, TLongObjectHashMap>[] methodCalledByConInterceptors; //TLongObjectHashMap contains Objects of type MethodByConInfo
protected HashMap<String, ArrayList<ArrayList<AdviceBinding>>> backrefMethodCalledByConstructorBindings = new HashMap<String, ArrayList<ArrayList<AdviceBinding>>>();
// caller pointcut support for constructors calling methods
protected HashMap<String, TLongObjectHashMap>[] conCalledByConBindings; // TLongObjectHashMap contains Objects of type ArrayList<AdviceBinding>
protected HashMap<String, TLongObjectHashMap>[] conCalledByConInterceptors; //TLongObjectHashMap contains Objects of type ConByConInfo
protected HashMap<String, ArrayList<ArrayList<AdviceBinding>>> backrefConCalledByConstructorBindings = new HashMap<String, ArrayList<ArrayList<AdviceBinding>>>();
// Used by instrumentor to access separate interceptor chains for read and write access
/** @deprecated Use fieldReadInfos instead*/
private Interceptor[][] fieldReadInterceptors;
protected FieldInfo[] fieldReadInfos;
/** @deprecated Use fieldWriteInfos instead */
private Interceptor[][] fieldWriteInterceptors;
protected FieldInfo[] fieldWriteInfos;
protected Field[] advisedFields;
//PER_JOINPOINT aspects for static fields or PER_CLASS_JOINPOINT aspects
//all apply to fields, and we need this since the same aspect should be used for
//read and write
private volatile HashMap<AspectDefinition, HashMap<FieldJoinpoint, Object>> fieldAspectsWithNoInstance = new HashMap<AspectDefinition, HashMap<FieldJoinpoint, Object>>();
protected boolean initialized = false;
public ClassAdvisor(String classname, AspectManager manager)
{
super(classname, manager);
}
public ClassAdvisor(Class<?> clazz, AspectManager manager)
{
this(clazz.getName(), manager);
this.clazz = clazz;
}
/**
* This method is to support PER_JOINPOINT scoping of Aspects for static fields
* Fields are special in that a get and set do not create separate aspect instances.
*
* Also used to support PER_CLASS_JOINPOINT, since that behaves similarly to static fields
*
* @param joinpoint
* @param def
* @return
*/
public Object getFieldAspect(FieldJoinpoint joinpoint, AspectDefinition def)
{
HashMap<FieldJoinpoint, Object> map = fieldAspectsWithNoInstance.get(def);
if (map == null)
{
synchronized (fieldAspectsWithNoInstance)
{
map = fieldAspectsWithNoInstance.get(def);
if (map == null)
{
map = new HashMap<FieldJoinpoint, Object>();
fieldAspectsWithNoInstance.put(def, map);
}
}
}
if (map.containsKey(joinpoint))
{
return map.get(joinpoint);
}
synchronized (map)
{
if (map.containsKey(joinpoint))
{
return map.get(joinpoint);
}
Object aspect = def.getFactory().createPerJoinpoint(this, joinpoint);
map.put(joinpoint, aspect);
return aspect;
}
}
public Field[] getAdvisedFields()
{
return advisedFields;
}
public TLongObjectHashMap getAdvisedMethods()
{
return advisedMethods;
}
protected TLongObjectHashMap getUnadvisedMethods()
{
return unadvisedMethods;
}
@Override
public Constructor<?>[] getConstructors()
{
return constructors;
}
public TLongObjectHashMap getMethodCalledByMethodInterceptors()
{
return getMethodByMethodData().getMethodCalledByMethodInterceptors();
}
public HashMap<String, TLongObjectHashMap>[] getMethodCalledByConInterceptors()
{
return methodCalledByConInterceptors;
}
public HashMap<String, TLongObjectHashMap>[] getConCalledByConInterceptors()
{
return conCalledByConInterceptors;
}
public TLongObjectHashMap getConCalledByMethodInterceptors()
{
return getConByMethodData().getConCalledByMethodInterceptors();
}
public TLongObjectHashMap getMethodCalledByMethodBindings()
{
return getMethodByMethodData().getMethodCalledByMethodBindings();
}
/** @deprecated use getFieldReadInfos instead */
public Interceptor[][] getFieldReadInterceptors()
{
throw new NotImplementedException("Use getFieldReadInfos");
}
public FieldInfo[] getFieldReadInfos()
{
return fieldReadInfos;
}
/** @deprecated use getFieldWriteInfos instead */
public Interceptor[][] getFieldWriteInterceptors()
{
throw new NotImplementedException("Use getFieldWriteInfos");
}
public FieldInfo[] getFieldWriteInfos()
{
return fieldWriteInfos;
}
public TLongObjectHashMap getMethodInterceptors()
{
return methodInfos.infos;
}
/**
* Constructs a new helper.
*/
public synchronized void attachClass(final Class<?> clazz)
{
if (initialized) return;
try
{
//long start = System.currentTimeMillis();
final AspectManager theManager = manager;
//register class loader: necessary when clazz was precompiled through aopc
manager.registerClassLoader(SecurityActions.getClassLoader(clazz));
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
{
public Object run() throws Exception
{
theManager.attachMetaData(ClassAdvisor.this, clazz);
interfaceIntroductions.clear();
// metadata should always come before creation of interceptor chain
// so that the interceptor factories have access to metadata.
// and so that metadata joinpoints can be checked
//
ClassAdvisor.this.clazz = clazz;
// Also metadata needs to be applied before applyIntroductionPointcuts because
// an annotation may be triggered by XML metadata as well as
// after populateMixinMethods so that proper metadata is applied to added methods
rebindClassMetaData();
theManager.applyInterfaceIntroductions(ClassAdvisor.this, clazz);
createFieldTable();
createMethodTables();
createConstructorTables();
populateMixinMethods();
// metadata should always come before creation of interceptor chain
// so that the interceptor factories have access to metadata.
// and so that metadata joinpoints can be checked
//
// Also metadata needs to be applied before applyIntroductionPointcuts because
// an annotation may be triggered by XML metadata as well as
// after populateMixinMethods so that proper metadata is applied to added methods
rebindClassMetaData();
createInterceptorChains();
initialized = true;
return null;
}
});
/*
// System.out.println("******************");
// System.out.println("attachClass: " + clazz.getName() + " took " + (System.currentTimeMillis() - start));
// System.out.println("******************");
*/
}
catch (PrivilegedActionException e)
{
throw new RuntimeException(e.getException());
}
}
/**
* Get method from clazz .If method not found,get the method
* from the clazz's parent.
*/
static private Method getMethod(Class<?> clazz, Method method) throws NoSuchMethodException
{
if ((clazz == null) || (clazz.equals(Object.class))) throw new NoSuchMethodException(method.getName());
try
{
String wrappedName = ClassAdvisor.notAdvisedMethodName(clazz.getName(), method.getName());
return clazz.getMethod(wrappedName, method.getParameterTypes());
}
catch (NoSuchMethodException e)
{
return getMethod(clazz.getSuperclass(), method);
}
}
/**
* Get a constructor's index in the class. Returns -1 if not there
*/
public int getConstructorIndex(Constructor<?> constructor)
{
for (int i = 0; i < constructors.length; i++)
{
if (constructor.equals(constructors[i]))
{
return i;
}
}
return -1;
}
/**
* Get a field's index in the class. Returns -1 if not there
*/
public int getFieldIndex(Field field)
{
for (int i = 0; i < advisedFields.length; i++)
{
if (field.equals(advisedFields[i]))
{
return i;
}
}
return -1;
}
/**
* Put mixin methods from mixin class into unadvisedMethods map so that
* they can be correctly invoked upon.
*/
protected void populateMixinMethods() throws Exception
{
ArrayList<InterfaceIntroduction> introductions = getInterfaceIntroductions();
for (InterfaceIntroduction introduction : introductions)
{
ArrayList<InterfaceIntroduction.Mixin> mixins = introduction.getMixins();
for (int i = 0; i < mixins.size(); i++)
{
InterfaceIntroduction.Mixin mixin = mixins.get(i);
ClassLoader cl = getClassLoader();
if (cl == null)
{
// Fall back to context classloader
cl = SecurityActions.getContextClassLoader();
}
cl.loadClass(mixin.getClassName());
String[] interfaces = mixin.getInterfaces();
for (int j = 0; j < interfaces.length; j++)
{
Class<?> intf = cl.loadClass(interfaces[j]);
if (intf.isAssignableFrom(clazz))//This is a fix for JBAOP-365. Class may have been woven, with the extra mixin information only available at init time
{
Method[] methods = intf.getMethods();
for (int k = 0; k < methods.length; k++)
{
//Put wrapped method in the class itself into the unadvisedMethods map
// String wrappedName = ClassAdvisor.notAdvisedMethodName(clazz.getName(), methods[k].getName());
// Method method = clazz.getMethod(wrappedName, methods[k].getParameterTypes());
Method method = getMethod(clazz, methods[k]);
long hash = MethodHashing.methodHash(method);
unadvisedMethods.put(hash, method);
}
}
}
}
}
}
@Override
public synchronized void removeAdviceBinding(AdviceBinding binding)
{
removeCallerPointcut(binding); // if binding is a caller remove references to it
super.removeAdviceBinding(binding);
}
@Override
public synchronized void removeAdviceBindings(ArrayList<AdviceBinding> bindings)
{
for (int i = 0; i < bindings.size(); i++)
{
AdviceBinding binding = bindings.get(i);
removeCallerPointcut(binding);
}
adviceBindings.removeAll(bindings);
rebuildInterceptors();
doesHaveAspects = adviceBindings.size() > 0;
}
protected void resolveFieldPointcut(FieldInfo[] newFieldInfos, Interceptor[][] interceptors, AdviceBinding binding, boolean write)
{
for (int i = 0; i < newFieldInfos.length; i++)
{
Field field = newFieldInfos[i].getField();
if ((!write && binding.getPointcut().matchesGet(this, field))
|| (write && binding.getPointcut().matchesSet(this, field)))
{
if (AspectManager.verbose) logger.debug("field matched " + ((write) ? "write" : "read") + " binding: " + field);
adviceBindings.add(binding);
binding.addAdvisor(this);
pointcutResolved(newFieldInfos[i], binding, new FieldJoinpoint(field));
if (AspectManager.maintainAdvisorMethodInterceptors && this.initialized)
{
interceptors[i] = newFieldInfos[i].getInterceptors();
}
}
}
}
protected void initializeMethodChain()
{
long[] keys = advisedMethods.keys();
for (int i = 0; i < keys.length; i++)
{
MethodInfo info = new MethodInfo();
Method amethod = (Method) advisedMethods.get(keys[i]);
info.setAdvisedMethod(amethod);
Method umethod = (Method) unadvisedMethods.get(keys[i]);
if (umethod == null) umethod = amethod;
info.setUnadvisedMethod(umethod);
info.setHash(keys[i]);
info.setAdvisor(this);
methodInfos.put(keys[i], info);
try
{
Field infoField = clazz.getDeclaredField(MethodExecutionTransformer.getMethodInfoFieldName(amethod.getName(), keys[i]));
infoField.setAccessible(true);
infoField.set(null, new WeakReference<MethodInfo>(info));
}
catch (NoSuchFieldException e)
{
// ignore, method may not be advised.
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
}
}
}
protected void initializeFieldReadChain()
{
this.fieldReadInfos = new FieldInfo[advisedFields.length];
for (int i = 0; i < advisedFields.length; i++)
{
FieldInfo info = new FieldInfo();
info.setAdvisedField(advisedFields[i]);
info.setAdvisor(this);
info.setIndex(i);
try
{
info.setWrapper(clazz.getDeclaredMethod(
FieldAccessTransformer.fieldRead(advisedFields[i].getName()),
new Class[] {Object.class}));
}
catch (NoSuchMethodException e)
{
//Just means not advised
}
fieldReadInfos[i] = info;
try
{
Field infoField = clazz.getDeclaredField(FieldAccessTransformer.getFieldReadInfoFieldName(advisedFields[i].getName()));
infoField.setAccessible(true);
infoField.set(null, new WeakReference<FieldInfo>(info));
}
catch (NoSuchFieldException e)
{
// ignore, method may not be advised.
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
}
protected void initializeFieldWriteChain()
{
this.fieldWriteInfos = new FieldInfo[advisedFields.length];
for (int i = 0; i < advisedFields.length; i++)
{
FieldInfo info = new FieldInfo();
info.setAdvisedField(advisedFields[i]);
info.setAdvisor(this);
info.setIndex(i);
try
{
info.setWrapper(clazz.getDeclaredMethod(
FieldAccessTransformer.fieldWrite(advisedFields[i].getName()),
new Class[] {Object.class, advisedFields[i].getType()}));
}
catch (NoSuchMethodException e)
{
//Just means not advised
}
fieldWriteInfos[i] = info;
try
{
Field infoField = clazz.getDeclaredField(FieldAccessTransformer.getFieldWriteInfoFieldName(advisedFields[i].getName()));
infoField.setAccessible(true);
infoField.set(null, new WeakReference<FieldInfo>(info));
}
catch (NoSuchFieldException e)
{
// ignore, method may not be advised.
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
}
// protected void finalizeChain(JoinPointInfo[] infos)
// {
// for (int i = 0; i < infos.length; i++)
// {
// JoinPointInfo info = infos[i];
// ArrayList<Interceptor> list = info.getInterceptorChain();
// Interceptor[] interceptors = null;
// if (list.size() > 0)
// {
// interceptors = applyPrecedence(list.toArray(new Interceptor[list.size()]));
// }
// info.setInterceptors(interceptors);
// }
// }
@SuppressWarnings("deprecation")
protected void createInterceptorChains() throws Exception
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("Creating chains for " + clazz + " " + getClassLoader());
}
// TODO flavia remove this
// this if is here because the subclass GeneratedClassAdvisor shouldn't be calling
// this method anymore: the infos are already created during this class initalization (initalise method)
// we call this method because we have the method chains
if (fieldReadInfos == null)
{
initializeFieldReadChain();
initializeFieldWriteChain();
initializeMethodChain();
}
initializeConstructorChain();
initializeConstructionChain();
resolveBindings(manager);
finalizeChains();
populateInterceptorsFromInfos();
doesHaveAspects = adviceBindings.size() > 0;
// Notify observer about this change
if (this.interceptorChainObserver != null)
{
this.interceptorChainObserver.interceptorChainsUpdated(fieldReadInfos,
fieldWriteInfos, constructorInfos, methodInfos);
}
}
@SuppressWarnings("deprecation")
private void resolveBindings(AspectManager manager)
{
ClassifiedBindingAndPointcutCollection bindingCol = manager.getBindingCollection();
for (AdviceBinding binding: bindingCol.getFieldReadBindings())
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("iterate binding " + binding.getName() + " " +
binding.getPointcut().getExpr());
}
resolveFieldPointcut(fieldReadInfos, fieldReadInterceptors, binding, false);
}
for (AdviceBinding binding: bindingCol.getFieldWriteBindings())
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("iterate binding " + binding.getName() + " " +
binding.getPointcut().getExpr());
}
resolveFieldPointcut(fieldWriteInfos, fieldWriteInterceptors, binding, true);
}
for (AdviceBinding binding: bindingCol.getConstructionBindings())
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("iterate binding " + binding.getName() + " " +
binding.getPointcut().getExpr());
}
resolveConstructionPointcut(binding);
}
for (AdviceBinding binding: bindingCol.getConstructorExecutionBindings())
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("iterate binding " + binding.getName() + " " +
binding.getPointcut().getExpr());
}
resolveConstructorPointcut(binding);
}
for (AdviceBinding binding: bindingCol.getMethodExecutionBindings())
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("iterate binding " + binding.getName() + " " +
binding.getPointcut().getExpr());
}
resolveMethodPointcut(binding);
}
}
protected void updateInterceptorChains() throws Exception
{
if (AspectManager.verbose && logger.isDebugEnabled())
{
logger.debug("Updating chains for " + clazz + " " + ((clazz != null) ? getClassLoader() : null ));
}
lockWriteChains();
try
{
resetChains();
resolveBindings(manager);
finalizeChains();
populateInterceptorsFromInfos();
}
finally
{
unlockWriteChains();
}
doesHaveAspects = adviceBindings.size() > 0;
// Notify observer about this change
if (this.interceptorChainObserver != null)
{
this.interceptorChainObserver.interceptorChainsUpdated(fieldReadInfos,
fieldWriteInfos, constructorInfos, methodInfos);
}
}
private void lockWriteChains()
{
lockWriteChain(methodInfos);
lockWriteChain(fieldReadInfos);
lockWriteChain(fieldWriteInfos);
lockWriteChain(constructorInfos);
lockWriteChain(constructionInfos);
}
private void unlockWriteChains()
{
unlockWriteChain(methodInfos);
unlockWriteChain(fieldReadInfos);
unlockWriteChain(fieldWriteInfos);
unlockWriteChain(constructorInfos);
unlockWriteChain(constructionInfos);
}
private void resetChains()
{
resetChain(methodInfos);
resetChain(fieldReadInfos);
resetChain(fieldWriteInfos);
resetChain(constructorInfos);
resetChain(constructionInfos);
}
private void resetChainsKeepInterceptors()
{
resetChainKeepInterceptors(methodInfos);
resetChainKeepInterceptors(fieldReadInfos);
resetChainKeepInterceptors(fieldWriteInfos);
resetChainKeepInterceptors(constructorInfos);
resetChainKeepInterceptors(constructionInfos);
}
protected void finalizeChains()
{
finalizeMethodChain();
finalizeChain(fieldReadInfos);
finalizeChain(fieldWriteInfos);
finalizeChain(constructorInfos);
finalizeChain(constructionInfos);
}
protected void resolvePointcuts(AdviceBinding binding)
{
if (AspectManager.verbose && logger.isDebugEnabled()) logger.debug("iterate binding " + binding.getName() + " " + binding.getPointcut().getExpr());
if (BindingClassifier.isExecution(binding))
{
resolveMethodPointcut(binding);
}
if (BindingClassifier.isGet(binding))
{
resolveFieldPointcut(fieldReadInfos, fieldReadInterceptors, binding, false);
}
if (BindingClassifier.isSet(binding))
{
resolveFieldPointcut(fieldWriteInfos, fieldWriteInterceptors, binding, true);
}
if (BindingClassifier.isConstructorExecution(binding))
{
resolveConstructorPointcut(binding);
}
if (BindingClassifier.isConstruction(binding))
{
resolveConstructionPointcut(binding);
}
}
@Override
protected void rebuildInterceptorsForRemovedBinding(AdviceBinding binding)
{
if (initialized)
{
if (System.getSecurityManager() == null)
{
RebuildInterceptorsAction.NON_PRIVILEGED.rebuildInterceptorsForRemovedBinding(this, binding);
}
else
{
RebuildInterceptorsAction.PRIVILEGED.rebuildInterceptorsForRemovedBinding(this, binding);
}
}
}
protected void doRebuildInterceptorsForRemovedBinding(AdviceBinding removedBinding)
{
lockWriteChains();
if (BindingClassifier.isExecution(removedBinding))
{
updateMethodPointcutAfterRemove(removedBinding);
}
if (BindingClassifier.isGet(removedBinding) || BindingClassifier.isSet(removedBinding))
{
updateFieldPointcutAfterRemove(fieldReadInfos, removedBinding, false);
updateFieldPointcutAfterRemove(fieldWriteInfos, removedBinding, true);
}
if (BindingClassifier.isConstructorExecution(removedBinding))
{
updateConstructorPointcutAfterRemove(removedBinding);
}
if (BindingClassifier.isConstruction(removedBinding))
{
updateConstructionPointcutAfterRemove(removedBinding);
}
finalizeChains();
unlockWriteChains();
// Notify observer about this change
if (this.interceptorChainObserver != null)
{
this.interceptorChainObserver.interceptorChainsUpdated(fieldReadInfos,
fieldWriteInfos, constructorInfos, methodInfos);
}
//TODO: optimize this
try
{
rebuildCallerInterceptors();
}
catch(Exception e)
{
}
}
@Override
protected void updateFieldPointcutAfterRemove(FieldInfo fieldInfo, int i, boolean write)
{
if (AspectManager.maintainAdvisorMethodInterceptors)
{
if (write)
{
this.fieldWriteInterceptors[i] = fieldInfo.getInterceptors();
}
else
{
this.fieldReadInterceptors[i] = fieldInfo.getInterceptors();
}
}
}
private MethodByConInfo initializeConstructorCallerInterceptorsMap(Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash, Method calledMethod) throws Exception
{
HashMap<String, TLongObjectHashMap> calledClassesMap = methodCalledByConInterceptors[callingIndex];
if (calledClassesMap == null)
{
calledClassesMap = new HashMap<String, TLongObjectHashMap>();
methodCalledByConInterceptors[callingIndex] = calledClassesMap;
}
TLongObjectHashMap calledMethodsMap = calledClassesMap.get(calledClass);
if (calledMethodsMap == null)
{
calledMethodsMap = new TLongObjectHashMap();
calledClassesMap.put(calledClass, calledMethodsMap);
}
//The standard MethodCalledByXXXXInvocation class calls by reflection and needs access
SecurityActions.setAccessible(calledMethod);
Class<?> calledClazz = getClassLoader().loadClass(calledClass);
MethodByConInfo info = new MethodByConInfo(this, calledClazz, callingClass, callingIndex, calledMethod, calledMethodHash, null);
calledMethodsMap.put(calledMethodHash, info);
return info;
}
private ConByConInfo initializeConCalledByConInterceptorsMap(Class<?> callingClass, int callingIndex, String calledClass, long calledConHash, Constructor<?> calledCon) throws Exception
{
HashMap<String, TLongObjectHashMap> calledClassesMap = conCalledByConInterceptors[callingIndex];
if (calledClassesMap == null)
{
calledClassesMap = new HashMap<String, TLongObjectHashMap>();
conCalledByConInterceptors[callingIndex] = calledClassesMap;
}
TLongObjectHashMap calledMethodsMap = calledClassesMap.get(calledClass);
if (calledMethodsMap == null)
{
calledMethodsMap = new TLongObjectHashMap();
calledClassesMap.put(calledClass, calledMethodsMap);
}
ConByConInfo info = createConByConInfo(callingClass, callingIndex, calledClass, calledCon, calledConHash);
calledMethodsMap.put(calledConHash, info);
return info;
}
private ConByConInfo createConByConInfo(Class<?> callingClass, int callingIndex, String calledClass, Constructor<?> calledCon, long calledConHash) throws Exception
{
//The standard ConstructorCalledByXXXXInvocation class calls by reflection and needs access
calledCon.setAccessible(true);
Class<?> calledClazz = getClassLoader().loadClass(calledClass);
try
{
int index = calledClass.lastIndexOf('.');
String baseClassName = calledClass.substring(index + 1);
Method wrapper = calledCon.getDeclaringClass().getDeclaredMethod(ConstructorExecutionTransformer.constructorFactory(baseClassName), calledCon.getParameterTypes());
return new ConByConInfo(this, calledClazz, callingClass, callingIndex, calledCon, calledConHash, wrapper, null);
}
catch (NoSuchMethodException e)
{
return new ConByConInfo(this, calledClazz, callingClass, callingIndex, calledCon, calledConHash, null, null);
}
}
protected void rebuildCallerInterceptors() throws Exception
{
if (methodByMethodData != null)
{
getMethodByMethodData().rebuildCallerInterceptors();
}
for (int i = 0; i < methodCalledByConInterceptors.length; i++)
{
HashMap<String, TLongObjectHashMap> calledClasses = methodCalledByConInterceptors[i];
if (calledClasses == null) continue;
for (Map.Entry<String, TLongObjectHashMap> entry : calledClasses.entrySet())
{
String cname = entry.getKey();
TLongObjectHashMap calledMethods = entry.getValue();
long[] calledKeys = calledMethods.keys();
for (int j = 0; j < calledKeys.length; j++)
{
long calledHash = calledKeys[j];
ArrayList<AdviceBinding> bindings = getConstructorCallerBindings(i, cname, calledHash);
bindConstructorCallerInterceptorChain(bindings, i, cname, calledHash);
}
}
}
if (conByMethodData != null)
{
getConByMethodData().rebuildCallerInterceptors();
}
for (int i = 0; i < conCalledByConInterceptors.length; i++)
{
HashMap<String, TLongObjectHashMap> calledClasses = conCalledByConInterceptors[i];
if (calledClasses == null) continue;
for (Map.Entry<String, TLongObjectHashMap> entry : calledClasses.entrySet())
{
String cname = entry.getKey();
TLongObjectHashMap calledMethods = entry.getValue();
long[] calledKeys = calledMethods.keys();
for (int j = 0; j < calledKeys.length; j++)
{
long calledHash = calledKeys[j];
ArrayList<AdviceBinding> bindings = getConCalledByConBindings(i, cname, calledHash);
bindConCalledByConInterceptorChain(bindings, i, cname, calledHash);
}
}
}
}
private ArrayList<AdviceBinding> getConstructorCallerBindings(int callingIndex, String cname, long calledHash)
{
try
{
Constructor<?> callingConstructor = constructors[callingIndex];
if (callingConstructor == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = getClassLoader().loadClass(cname);
Method calledMethod = MethodHashing.findMethodByHash(called, calledHash);
if (calledMethod == null) throw new RuntimeException("Unable to figure out calledmethod of a caller pointcut");
Collection<AdviceBinding> bindings = manager.getBindingCollection().
getMethodCallBindings();
ArrayList<AdviceBinding> result = new ArrayList<AdviceBinding>(bindings.size());
for(AdviceBinding ab : bindings)
{
if (ab.getPointcut().matchesCall(this, callingConstructor, called, calledMethod))
{
result.add(ab);
}
}
return result;
}
catch(Exception e)
{
logger.error("Error happened with methodCallConBinding",e);
return new ArrayList<AdviceBinding>();
}
}
private ArrayList<AdviceBinding> getConCalledByConBindings(int callingIndex, String cname, long calledHash)
{
try
{
Constructor<?> callingConstructor = constructors[callingIndex];
if (callingConstructor == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = getClassLoader().loadClass(cname);
Constructor<?> calledCon = MethodHashing.findConstructorByHash(called, calledHash);
if (calledCon == null) throw new RuntimeException("Unable to figure out calledcon of a caller pointcut");
Collection<AdviceBinding> bindings = manager.getBindingCollection().
getConstructorCallBindings();
ArrayList<AdviceBinding> result= new ArrayList<AdviceBinding>(bindings.size());
for(AdviceBinding ab : bindings)
{
if (ab.getPointcut().matchesCall(this, callingConstructor, called, calledCon))
{
result.add(ab);
}
}
return result;
}
catch(Exception e)
{
logger.error("Error happened for conCalledConBindings", e);
return new ArrayList<AdviceBinding>();
}
}
protected void finalizeMethodCalledByMethodInterceptorChain(MethodByMethodInfo info)
{
adjustInfoForAddedBinding(info);
ArrayList<Interceptor> list = info.getInterceptorChain();
Interceptor[] interceptors = null;
if (list.size() > 0)
{
interceptors = list.toArray(new Interceptor[list.size()]);
}
info.setInterceptors(interceptors);
}
protected void finalizeConCalledByMethodInterceptorChain(ConByMethodInfo info)
{
adjustInfoForAddedBinding(info);
ArrayList<Interceptor> list = info.getInterceptorChain();
Interceptor[] interceptors = null;
if (list.size() > 0)
{
interceptors = list.toArray(new Interceptor[list.size()]);
}
info.setInterceptors(interceptors);
}
private void bindConCalledByConInterceptorChain(ArrayList<AdviceBinding> bindings, int callingIndex, String cname, long calledHash)
{
ConByConInfo info = getConCalledByCon(callingIndex, cname, calledHash);
info.clear();
for (AdviceBinding binding : bindings)
{
if (BindingClassifier.isConstructorCall(binding))
{
pointcutResolved(info, binding, new ConstructorCalledByConstructorJoinpoint(info.getCallingConstructor(), info.getConstructor()));
}
}
finalizeConCalledByConInterceptorChain(info);
}
protected void finalizeConCalledByConInterceptorChain(ConByConInfo info)
{
adjustInfoForAddedBinding(info);
ArrayList<Interceptor> list = info.getInterceptorChain();
Interceptor[] interceptors = null;
if (list.size() > 0)
{
interceptors = list.toArray(new Interceptor[list.size()]);
}
info.setInterceptors(interceptors);
}
private void bindConstructorCallerInterceptorChain(ArrayList<AdviceBinding> bindings, int callingIndex, String cname, long calledHash)
{
MethodByConInfo info = getConstructorCallerMethodInfo(callingIndex, cname, calledHash);
info.clear();
for (AdviceBinding binding : bindings)
{
if (BindingClassifier.isCall(binding))
{
pointcutResolved(info, binding, new MethodCalledByConstructorJoinpoint(info.getCallingConstructor(), info.getMethod()));
}
}
finalizeMethodCalledByConInterceptorChain(info);
}
protected void finalizeMethodCalledByConInterceptorChain(MethodByConInfo info)
{
adjustInfoForAddedBinding(info);
ArrayList<Interceptor> list = info.getInterceptorChain();
Interceptor[] interceptors = null;
if (list.size() > 0)
{
interceptors = list.toArray(new Interceptor[list.size()]);
}
info.setInterceptors(interceptors);
}
@Override
protected void rebuildInterceptors()
{
if (initialized)
{
if (System.getSecurityManager() == null)
{
RebuildInterceptorsAction.NON_PRIVILEGED.rebuildInterceptors(this);
}
else
{
RebuildInterceptorsAction.PRIVILEGED.rebuildInterceptors(this);
}
}
}
protected void doRebuildInterceptors()
{
try
{
adviceBindings.clear();
if (!this.initialized)
{
createInterceptorChains();
}
else
{
updateInterceptorChains();
}
rebuildCallerInterceptors();
}
catch (Exception ex)
{
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
protected void rebuildInterceptorsForAddedBinding(AdviceBinding binding)
{
if (initialized)
{
if (System.getSecurityManager() == null)
{
RebuildInterceptorsAction.NON_PRIVILEGED.rebuildInterceptorsForAddedBinding(this, binding);
}
else
{
RebuildInterceptorsAction.PRIVILEGED.rebuildInterceptorsForAddedBinding(this, binding);
}
}
}
protected void doRebuildInterceptorsForAddedBinding(AdviceBinding binding)
{
try
{
if (!this.initialized)
{
throw new IllegalStateException("This should only be called when adding bindings to an exisiting advisor");
}
lockWriteChains();
try
{
resetChainsKeepInterceptors();
resolvePointcuts(binding);
finalizeChains();
}
finally
{
unlockWriteChains();
}
//TODO: Optimize this
rebuildCallerInterceptors();
if (interceptorChainObserver != null)
{
this.interceptorChainObserver.interceptorChainsUpdated(fieldReadInfos,
fieldWriteInfos, constructorInfos, methodInfos);
}
}
catch (Exception ex)
{
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
protected void bindClassMetaData(ClassMetaDataBinding data)
{
try
{
ClassMetaDataLoader loader = data.getLoader();
Object[] objs = advisedMethods.getValues();
Method[] methods = new Method[objs.length];
Field[] fields = advisedFields;
// set to empty array because advisedFields may not have been initialized yet
if (fields == null) fields = new Field[0];
Constructor<?>[] cons = constructors;
// set to empty array because constructors may not have been initialized yet
if (cons == null) cons = new Constructor[0];
for (int i = 0; i < objs.length; i++) methods[i] = (Method) objs[i];
loader.bind(this, data, methods, fields, cons);
}
catch (Exception ex)
{
// REVISIT: Need to know how errors affects deployment
ex.printStackTrace();
}
}
protected void rebindClassMetaData()
{
defaultMetaData.clear();
methodMetaData.clear();
fieldMetaData.clear();
constructorMetaData.clear();
classMetaData.clear();
for (int i = 0; i < classMetaDataBindings.size(); i++)
{
ClassMetaDataBinding data = classMetaDataBindings.get(i);
bindClassMetaData(data);
}
deployAnnotationOverrides();
}
public synchronized void addClassMetaData(ClassMetaDataBinding data)
{
initClassMetaDataBindingsList();
classMetaDataBindings.add(data);
if (this.clazz == null) return; // don't bind till later.
bindClassMetaData(data);
// Recalculate interceptorPointcuts because of MetaDataInterceptorPointcuts
adviceBindings.clear();
doesHaveAspects = false;
rebuildInterceptors();
}
public synchronized void removeClassMetaData(ClassMetaDataBinding data)
{
if (classMetaDataBindings.remove(data))
{
if (this.clazz == null) return; // not bound yet
rebindClassMetaData();
// Recalculate interceptorPointcuts because of MetaDataInterceptorPointcuts
adviceBindings.clear();
doesHaveAspects = false;
rebuildInterceptors();
}
}
private void initializeEmptyConstructorCallerChain(int callingIndex, String calledClass, long calledMethodHash) throws Exception
{
HashMap<String, TLongObjectHashMap> callingCon = methodCalledByConBindings[callingIndex];
if (callingCon == null)
{
callingCon = new HashMap<String, TLongObjectHashMap>();
methodCalledByConBindings[callingIndex] = callingCon;
}
TLongObjectHashMap classMap = callingCon.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingCon.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledMethodHash);
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledMethodHash, bindings);
}
}
private void initializeConCalledByConEmptyChain(int callingIndex, String calledClass, long calledConHash) throws Exception
{
HashMap<String, TLongObjectHashMap> callingCon = conCalledByConBindings[callingIndex];
if (callingCon == null)
{
callingCon = new HashMap<String, TLongObjectHashMap>();
conCalledByConBindings[callingIndex] = callingCon;
}
TLongObjectHashMap classMap = callingCon.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingCon.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledConHash);
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledConHash, bindings);
}
}
public void addConstructorCallerPointcut(int callingIndex, String calledClass, long calledMethodHash, AdviceBinding binding) throws Exception
{
if (AspectManager.verbose) logger.debug("constructor call matched binding " + binding.getPointcut().getExpr());
adviceBindings.add(binding);
binding.addAdvisor(this);
HashMap<String, TLongObjectHashMap> callingCon = methodCalledByConBindings[callingIndex];
if (callingCon == null)
{
callingCon = new HashMap<String, TLongObjectHashMap>();
methodCalledByConBindings[callingIndex] = callingCon;
}
TLongObjectHashMap classMap = callingCon.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingCon.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledMethodHash);
boolean createdBindings = false;
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledMethodHash, bindings);
createdBindings = true;
}
if (!bindings.contains(binding)) bindings.add(binding);
// this is so that we can undeploy a caller
ArrayList<ArrayList<AdviceBinding>> backrefs = backrefMethodCalledByConstructorBindings.get(binding.getName());
if (backrefs == null)
{
backrefs = new ArrayList<ArrayList<AdviceBinding>>();
backrefMethodCalledByConstructorBindings.put(binding.getName(), backrefs);
backrefs.add(bindings);
}
else if (createdBindings) backrefs.add(bindings);
}
private void addConstructorCalledByConPointcut(int callingIndex, String calledClass, long calledConHash, AdviceBinding binding) throws Exception
{
if (AspectManager.verbose) logger.debug("constructor call matched binding " + binding.getPointcut().getExpr());
adviceBindings.add(binding);
binding.addAdvisor(this);
HashMap<String, TLongObjectHashMap> callingCon = conCalledByConBindings[callingIndex];
if (callingCon == null)
{
callingCon = new HashMap<String, TLongObjectHashMap>();
conCalledByConBindings[callingIndex] = callingCon;
}
TLongObjectHashMap classMap = callingCon.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingCon.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledConHash);
boolean createdBindings = false;
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledConHash, bindings);
createdBindings = true;
}
if (!bindings.contains(binding)) bindings.add(binding);
// this is so that we can undeploy a caller
ArrayList<ArrayList<AdviceBinding>> backrefs = backrefConCalledByConstructorBindings.get(binding.getName());
if (backrefs == null)
{
backrefs = new ArrayList<ArrayList<AdviceBinding>>();
backrefConCalledByConstructorBindings.put(binding.getName(), backrefs);
backrefs.add(bindings);
}
else if (createdBindings) backrefs.add(bindings);
}
private void removeCallerPointcut(AdviceBinding binding)
{
getMethodByMethodData().removeCallerPointcut(binding);
}
/**
* Generates internal, unadvised version of a method name.
*/
public static String notAdvisedMethodName(String className,
String methodName)
{
return className.replace('.', '$') + "$" + methodName +
NOT_TRANSFORMABLE_SUFFIX;
}
/**
* Is this the name of a private, unadvised thing?
*/
public static boolean isWithoutAdvisement(String name)
{
return name.endsWith(NOT_TRANSFORMABLE_SUFFIX);
}
/**
* Is the method advisable?
*/
public static boolean isAdvisable(Method method)
{
// note: this should match the implementation in the instrumentor.
int modifiers = method.getModifiers();
return (
!isWithoutAdvisement(method.getName()) &&
!Modifier.isAbstract(modifiers) &&
!Modifier.isNative(modifiers) &&
!(method.getName().equals("_getAdvisor") &&
method.getParameterTypes().length == 0 &&
method.getReturnType().equals(Advisor.class)) &&
!(method.getName().equals("_getClassAdvisor") &&
method.getParameterTypes().length == 0 &&
method.getReturnType().equals(Advisor.class)) &&
!(method.getName().equals("_getInstanceAdvisor") &&
method.getParameterTypes().length == 0 &&
method.getReturnType().equals(InstanceAdvisor.class)) &&
!(method.getName().equals("_setInstanceAdvisor") &&
method.getParameterTypes().length == 1 &&
method.getParameterTypes()[0].equals(InstanceAdvisor.class)) &&
ReflectUtils.isNotAccessMethod(method));
}
private void populateFieldTable(ArrayList<Field> fields, Class<?> superclass)
throws Exception
{
if (superclass == null) return;
if (superclass.equals(Object.class)) return;
populateFieldTable(fields, superclass.getSuperclass());
// if (!isAdvised(superclass)) return;
ArrayList<Field> temp = new ArrayList<Field>();
Field[] declaredFields = superclass.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++)
{
if (Advisable.isAdvisable(declaredFields[i]))
{
// Need to do this because notadvisable fields maybe private or protected
declaredFields[i].setAccessible(true);
temp.add(declaredFields[i]);
}
}
Collections.sort(temp, FieldComparator.INSTANCE);
fields.addAll(temp);
}
/**
* Gets advised methods.
*/
protected void createFieldTable() throws Exception
{
ArrayList<Field> fields = new ArrayList<Field>();
populateFieldTable(fields, clazz);
advisedFields = fields.toArray(new Field[fields.size()]);
}
protected void addDeclaredMethods(Class<?> superclass) throws Exception
{
Method[] declaredMethods = superclass.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++)
{
if (ClassAdvisor.isAdvisable(declaredMethods[i]))
{
long hash = MethodHashing.methodHash(declaredMethods[i]);
advisedMethods.put(hash, declaredMethods[i]);
try
{
Method m = declaredMethods[i];
Method un = superclass.getDeclaredMethod(ClassAdvisor.notAdvisedMethodName(superclass.getName(),
m.getName()),
m.getParameterTypes());
un.setAccessible(true);
unadvisedMethods.put(hash, un);
}
catch (NoSuchMethodException ignored)
{
}
}
}
}
/**
* Create a HashMap of method hash and Method
* Superclasses get added first so subclasses will override with
* correct overriden method
*/
private void populateMethodTables(Class<?> superclass)
throws Exception
{
if (superclass == null) return;
if (superclass.equals(Object.class)) return;
populateMethodTables(superclass.getSuperclass());
//The advisor for the superclass may be a container
Advisor superAdvisor = manager.getAnyAdvisorIfAdvised(superclass);
if (superAdvisor != null && superAdvisor instanceof ClassAdvisor)
{
TLongObjectHashMap superHash = ((ClassAdvisor)superAdvisor).getUnadvisedMethods();
long[] keys = superHash.keys();
for (int i = 0; i < keys.length; i++)
{
unadvisedMethods.put(keys[i], superHash.get(keys[i]));
}
}
addDeclaredMethods(superclass);
}
protected void createMethodTables()
throws Exception
{
initAdvisedMethodsMap();
populateMethodTables(clazz.getSuperclass());
addDeclaredMethods(clazz);
}
protected void createConstructorTables() throws Exception
{
constructors = clazz.getDeclaredConstructors();
methodCalledByConBindings = new HashMap[constructors.length];
methodCalledByConInterceptors = new HashMap[constructors.length];
conCalledByConBindings = new HashMap[constructors.length];
conCalledByConInterceptors = new HashMap[constructors.length];
for (int i = 0; i < constructors.length; i++)
{
constructors[i].setAccessible(true);
}
Arrays.sort(constructors, ConstructorComparator.INSTANCE);
}
public MethodByMethodInfo resolveCallerMethodInfo(long callingMethodHash, String calledClass, long calledMethodHash)
{
return getMethodByMethodData().resolveCallerMethodInfo(callingMethodHash, calledClass, calledMethodHash);
}
public WeakReference<MethodByMethodInfo> resolveCallerMethodInfoAsWeakReference(long callingMethodHash, String calledClass, long calledMethodHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<MethodByMethodInfo>(resolveCallerMethodInfo(callingMethodHash, calledClass, calledMethodHash));
}
public ConByMethodInfo resolveCallerConstructorInfo(long callingMethodHash, String calledClass, long calledConHash)
{
return getConByMethodData().resolveCallerConstructorInfo(callingMethodHash, calledClass, calledConHash);
}
public WeakReference<ConByMethodInfo> resolveCallerConstructorInfoAsWeakReference(long callingMethodHash, String calledClass, long calledConHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<ConByMethodInfo>(resolveCallerConstructorInfo(callingMethodHash, calledClass, calledConHash));
}
@Deprecated
public MethodByConInfo resolveConstructorCallerMethodInfo(int callingIndex, String calledClass, long calledMethodHash)
{
return resolveConstructorCallerMethodInfo(this.getClazz(), callingIndex, calledClass, calledMethodHash);
}
public MethodByConInfo resolveConstructorCallerMethodInfo(Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash)
{
if (System.getSecurityManager() == null)
{
return ResolveConstructorCallerMethodInfoAction.NON_PRIVILEGED.resolveInfo(this, callingClass, callingIndex, calledClass, calledMethodHash);
}
else
{
return ResolveConstructorCallerMethodInfoAction.PRIVILEGED.resolveInfo(this, callingClass, callingIndex, calledClass, calledMethodHash);
}
}
private MethodByConInfo doResolveConstructorCallerMethodInfo(Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash)
{
try
{
Constructor<?> callingConstructor = constructors[callingIndex];
if (callingConstructor == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = SecurityActions.getClassLoader(callingClass).loadClass(calledClass);
Method calledMethod = MethodHashing.findMethodByHash(called, calledMethodHash);
if (calledMethod == null) throw new RuntimeException("Unable to figure out calledmethod of a caller pointcut");
boolean matched = false;
ClassifiedBindingAndPointcutCollection bindingCol = manager.getBindingCollection();
for (AdviceBinding binding : bindingCol.getConstructorCallBindings())
{
if (binding.getPointcut().matchesCall(this, callingConstructor, called, calledMethod))
{
addConstructorCallerPointcut(callingIndex, calledClass, calledMethodHash, binding);
matched = true;
}
}
if (!matched) initializeEmptyConstructorCallerChain(callingIndex, calledClass, calledMethodHash);
MethodByConInfo info = initializeConstructorCallerInterceptorsMap(callingClass, callingIndex, calledClass, calledMethodHash, calledMethod);
ArrayList<AdviceBinding> bindings = getConstructorCallerBindings(callingIndex, calledClass, calledMethodHash);
bindConstructorCallerInterceptorChain(bindings, callingIndex, calledClass, calledMethodHash);
return info;
}
catch (Exception x)
{
throw new RuntimeException(x);
}
}
@Deprecated
public WeakReference<MethodByConInfo> resolveConstructorCallerMethodInfoAsWeakReference(int callingIndex, String calledClass, long calledMethodHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<MethodByConInfo>(resolveConstructorCallerMethodInfo(callingIndex, calledClass, calledMethodHash));
}
public WeakReference<MethodByConInfo> resolveConstructorCallerMethodInfoAsWeakReference(Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<MethodByConInfo>(resolveConstructorCallerMethodInfo(callingClass, callingIndex, calledClass, calledMethodHash));
}
public ConByConInfo resolveConstructorCallerConstructorInfo(int callingIndex, String calledClass, long calledConHash)
{
return resolveConstructorCallerConstructorInfo(this.getClazz(), callingIndex, calledClass, calledConHash);
}
public ConByConInfo resolveConstructorCallerConstructorInfo(Class<?> callingClass, int callingIndex, String calledClass, long calledConHash)
{
if (System.getSecurityManager() == null)
{
return ResolveConstructorCallerConstructorInfoAction.NON_PRIVILEGED.resolveInfo(this, callingClass, callingIndex, calledClass, calledConHash);
}
else
{
return ResolveConstructorCallerConstructorInfoAction.PRIVILEGED.resolveInfo(this, callingClass, callingIndex, calledClass, calledConHash);
}
}
private ConByConInfo doResolveConstructorCallerConstructorInfo(Class<?> callingClass, int callingIndex, String calledClass, long calledConHash)
{
try
{
Constructor<?> callingConstructor = constructors[callingIndex];
if (callingConstructor == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = SecurityActions.getClassLoader(callingClass).loadClass(calledClass);
Constructor<?> calledCon = MethodHashing.findConstructorByHash(called, calledConHash);
if (calledCon == null) throw new RuntimeException("Unable to figure out calledcon of a caller pointcut");
boolean matched = false;
ClassifiedBindingAndPointcutCollection bindingCol = manager.getBindingCollection();
for (AdviceBinding binding : bindingCol.getConstructorCallBindings())
{
if (binding.getPointcut().matchesCall(this, callingConstructor, called, calledCon))
{
addConstructorCalledByConPointcut(callingIndex, calledClass, calledConHash, binding);
matched = true;
}
}
if (!matched) initializeConCalledByConEmptyChain(callingIndex, calledClass, calledConHash);
ConByConInfo info = initializeConCalledByConInterceptorsMap(callingClass, callingIndex, calledClass, calledConHash, calledCon);
ArrayList<AdviceBinding> bindings = getConCalledByConBindings(callingIndex, calledClass, calledConHash);
bindConCalledByConInterceptorChain(bindings, callingIndex, calledClass, calledConHash);
return info;
}
catch (Exception x)
{
throw new RuntimeException(x);
}
}
@Deprecated
public WeakReference<ConByConInfo> resolveConstructorCallerConstructorInfoAsWeakReference(int callingIndex, String calledClass, long calledConHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<ConByConInfo>(resolveConstructorCallerConstructorInfo(callingIndex, calledClass, calledConHash));
}
public WeakReference<ConByConInfo> resolveConstructorCallerConstructorInfoAsWeakReference(Class<?> callingClass, int callingIndex, String calledClass, long calledConHash)
{
//Javassist doesn't like this in a field initialiser hence this method
return new WeakReference<ConByConInfo>(resolveConstructorCallerConstructorInfo(callingClass, callingIndex, calledClass, calledConHash));
}
/////////////////////////
// Invoking
/**
* Invokes target object without applying interceptors.
*/
public Object invokeWithoutAdvisement(Object target, long methodHash,
Object[] arguments) throws Throwable
{
try
{
Method method = (Method) unadvisedMethods.get(methodHash);
return method.invoke(target, arguments);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
}
public Object invokeNewWithoutAdvisement(Object[] arguments, Constructor<?> constructor) throws Throwable
{
try
{
return constructor.newInstance(arguments);
}
catch (InstantiationException in)
{
throw new RuntimeException("failed to call constructor", in);
}
catch (IllegalAccessException ill)
{
throw new RuntimeException("illegal access", ill);
}
catch (InvocationTargetException ite)
{
throw ite.getCause();
}
}
public Object invokeMethod(long methodHash, Object[] arguments) throws Throwable
{
return invokeMethod(null, methodHash, arguments);
}
public Object invokeMethod(Object target, long methodHash, Object[] arguments) throws Throwable
{
InstanceAdvisor advisor = null;
if (target != null)
{
InstanceAdvised advised = (InstanceAdvised) target;
advisor = advised._getInstanceAdvisor();
}
MethodInfo info = methodInfos.getMethodInfo(methodHash);
return invokeMethod(advisor, target, methodHash, arguments, info);
}
public Object invokeMethod(InstanceAdvisor instanceAdvisor, Object target, long methodHash, Object[] arguments)
throws Throwable
{
MethodInfo info = methodInfos.getMethodInfo(methodHash);
if (info == null && logger.isDebugEnabled())
{
logger.debug("info is null for hash: " + methodHash + " of " + clazz.getName());
}
return invokeMethod(instanceAdvisor, target, methodHash, arguments, info);
}
public Object invokeMethod(InstanceAdvisor instanceAdvisor, Object target, long methodHash, Object[] arguments, MethodInfo info)
throws Throwable
{
Interceptor[] aspects = info.getInterceptors();
if (instanceAdvisor != null && (instanceAdvisor.hasInterceptors()))
{
aspects = instanceAdvisor.getInterceptors(aspects);
}
MethodInvocation invocation = new MethodInvocation(info, aspects);
invocation.setArguments(arguments);
invocation.setTargetObject(target);
return invocation.invokeNext();
}
/**
*@deprecated
*/
public Object invokeCaller(long callingMethodHash, Object target, Object[] args, CallerMethodInfo info, Object callingObject)
throws Throwable
{
return invokeCaller((MethodByMethodInfo)info, callingObject, target, args);
}
public Object invokeCaller(MethodByMethodInfo info, Object callingObject, Object target, Object[] args) throws Throwable
{
MethodCalledByMethodInvocation invocation = new MethodCalledByMethodInvocation(info, callingObject, target, args, info.getInterceptors());
invocation.setTargetObject(target);
return invocation.invokeNext();
}
/**
*@deprecated
*/
public Object invokeConCalledByMethod(long callingMethodHash, Object[] args, CallerConstructorInfo info, Object callingObject)
throws Throwable
{
return invokeConCalledByMethod((ConByMethodInfo)info, callingObject, args);
}
public Object invokeConCalledByMethod(ConByMethodInfo info, Object callingObject, Object[] args)
throws Throwable
{
ConstructorCalledByMethodInvocation invocation = new ConstructorCalledByMethodInvocation(info, callingObject, args, info.getInterceptors());
return invocation.invokeNext();
}
/**
*@deprecated
*/
public Object invokeConstructorCaller(int callingIndex, Object target, Object[] args, CallerMethodInfo info)
throws Throwable
{
return invokeConstructorCaller((MethodByConInfo)info, null, target, args);
}
/**
*@deprecated
*/
public Object invokeConstructorCaller(int callingIndex, Object callingObject, Object target, Object[] args, CallerMethodInfo info)
throws Throwable
{
return invokeConstructorCaller((MethodByConInfo)info, callingObject, target, args);
}
/**
* @deprecated
*
* Prefer using the version with <code>callingObject</code> instead of this one,
* since this object is available for call invocations made inside constructors.
*
* @see #invokeConstructorCaller(MethodByConInfo, Object, Object, Object[])
*/
public Object invokeConstructorCaller(MethodByConInfo info, Object target, Object[] args)
throws Throwable
{
return this.invokeConstructorCaller(info, null, target, args);
}
public Object invokeConstructorCaller(MethodByConInfo info, Object callingObject, Object target, Object[] args)
throws Throwable
{
MethodCalledByConstructorInvocation invocation = new MethodCalledByConstructorInvocation(info, callingObject, target, args, info.getInterceptors());
invocation.setTargetObject(target);
return invocation.invokeNext();
}
/**
*@deprecated
*/
public Object invokeConCalledByCon(int callingIndex, Object[] args, CallerConstructorInfo info)
throws Throwable
{
return invokeConCalledByCon((ConByConInfo)info, null, args);
}
/**
*@deprecated
*/
public Object invokeConCalledByCon(int callingIndex, Object callingObject, Object[] args, CallerConstructorInfo info)
throws Throwable
{
return invokeConCalledByCon((ConByConInfo)info, callingObject, args);
}
/**
* @deprecated
*
* Prefer using the version with <code>callingObject</code> instead of this one,
* since this object is available for call invocations made inside constructors.
*
* @see #invokeConCalledByCon(ConByConInfo, Object, Object[])
*/
public Object invokeConCalledByCon(ConByConInfo info, Object[] args)
throws Throwable
{
return invokeConCalledByCon(info, null, args);
}
public Object invokeConCalledByCon(ConByConInfo info, Object callingObject, Object[] args)
throws Throwable
{
ConstructorCalledByConstructorInvocation invocation = new ConstructorCalledByConstructorInvocation(info, callingObject, args, info.getInterceptors());
return invocation.invokeNext();
}
private MethodByConInfo getConstructorCallerMethodInfo(int callingIndex, String calledClass, long calledMethodHash)
{
HashMap<String, TLongObjectHashMap> calledClasses = methodCalledByConInterceptors[callingIndex];
TLongObjectHashMap calledMethods = calledClasses.get(calledClass);
MethodByConInfo info = (MethodByConInfo) calledMethods.get(calledMethodHash);
return info;
}
private ConByConInfo getConCalledByCon(int callingIndex, String calledClass, long calledConHash)
{
HashMap<String, TLongObjectHashMap> calledClasses = conCalledByConInterceptors[callingIndex];
TLongObjectHashMap calledMethods = calledClasses.get(calledClass);
ConByConInfo info = (ConByConInfo) calledMethods.get(calledConHash);
return info;
}
public Object invokeNew(Object[] args, int idx) throws Throwable
{
Interceptor[] cInterceptors = constructorInfos[idx].getInterceptors();
if (cInterceptors == null) cInterceptors = new Interceptor[0];
ConstructorInvocation invocation = new ConstructorInvocation(cInterceptors);
invocation.setAdvisor(this);
invocation.setArguments(args);
invocation.setConstructor(constructors[idx]);
return invocation.invokeNext();
}
/**
* Invokes interceptor chain.
* This is the beginning
*/
public Object invokeRead(Object target, int index)
throws Throwable
{
Interceptor[] aspects = fieldReadInfos[index].getInterceptors();
if (aspects == null) aspects = new Interceptor[0];
FieldReadInvocation invocation;
if (target != null)
{
InstanceAdvised advised = (InstanceAdvised) target;
InstanceAdvisor advisor = advised._getInstanceAdvisor();
if (advisor != null && advisor.hasInterceptors())
{
aspects = advisor.getInterceptors(aspects);
}
}
invocation = new FieldReadInvocation(advisedFields[index], index, aspects);
invocation.setAdvisor(this);
invocation.setTargetObject(target);
return invocation.invokeNext();
}
/**
* Invokes interceptor chain.
* This is the beginning
*/
public Object invokeWrite(Object target, int index, Object value)
throws Throwable
{
Interceptor[] aspects = fieldWriteInfos[index].getInterceptors();
if (aspects == null) aspects = new Interceptor[0];
FieldWriteInvocation invocation;
if (target != null)
{
InstanceAdvised advised = (InstanceAdvised) target;
InstanceAdvisor advisor = advised._getInstanceAdvisor();
if (advisor != null && advisor.hasInterceptors())
{
aspects = advised._getInstanceAdvisor().getInterceptors(aspects);
}
}
invocation = new FieldWriteInvocation(advisedFields[index], index, value, aspects);
invocation.setAdvisor(this);
invocation.setTargetObject(target);
return invocation.invokeNext();
}
/**
* Invokes interceptor chain.
* This is the beginning
*/
public Object invoke(Invocation invocation) throws Throwable
{
if (invocation instanceof FieldWriteInvocation)
{
FieldWriteInvocation fieldInvocation = (FieldWriteInvocation) invocation;
Object target = fieldInvocation.getTargetObject();
Object val = fieldInvocation.getValue();
Field field = fieldInvocation.getField();
field.set(target, val);
return null;
}
else if (invocation instanceof FieldReadInvocation)
{
FieldReadInvocation fieldInvocation = (FieldReadInvocation) invocation;
Object target = fieldInvocation.getTargetObject();
Field field = fieldInvocation.getField();
return field.get(target);
}
else if (invocation instanceof MethodInvocation)
{
MethodInvocation methodInvocation = (MethodInvocation) invocation;
return invokeWithoutAdvisement(methodInvocation.getTargetObject(),
methodInvocation.getMethodHash(),
methodInvocation.getArguments());
}
else if (invocation instanceof ConstructorInvocation)
{
ConstructorInvocation cInvocation = (ConstructorInvocation) invocation;
Object[] arguments = cInvocation.getArguments();
Constructor<?> constructor = cInvocation.getConstructor();
return invokeNewWithoutAdvisement(arguments, constructor);
}
throw new IllegalStateException("Unknown Invocation type: " + invocation.getClass().getName());
}
@Override
public void cleanup()
{
super.cleanup();
if (methodByMethodData != null)
{
methodByMethodData.cleanup();
methodByMethodData = null;
}
if (conByMethodData != null)
{
conByMethodData.cleanup();
conByMethodData = null;
}
}
// interceptor chain observer
private InterceptorChainObserver interceptorChainObserver;
/**
* Returns the interceptor chain observer associated with this advisor.
*/
protected InterceptorChainObserver getInterceptorChainObserver()
{
return this.interceptorChainObserver;
}
/**
* Defines the interceptor chain observer associated with this advisor.
* @param observer the interceptor chain observer.
*/
protected void setInterceptorChainObserver(InterceptorChainObserver observer)
{
if (observer != null)
{
observer.initialInterceptorChains(this.clazz, fieldReadInfos,
fieldWriteInfos, constructorInfos, methodInfos);
}
this.interceptorChainObserver = observer;
}
/** @deprecated We should just be using xxxxInfos */
@Override
protected void populateInterceptorsFromInfos()
{
if (!AspectManager.maintainAdvisorMethodInterceptors)
{
return;
}
super.populateInterceptorsFromInfos();
fieldReadInterceptors = new Interceptor[fieldReadInfos.length][];
for (int i = 0 ; i < fieldReadInfos.length ; i++)
{
fieldReadInterceptors[i] = fieldReadInfos[i].getInterceptors();
}
fieldWriteInterceptors = new Interceptor[fieldWriteInfos.length][];
for (int i = 0 ; i < fieldWriteInfos.length ; i++)
{
fieldWriteInterceptors[i] = fieldWriteInfos[i].getInterceptors();
}
if (constructionInfos == null)
{
constructionInterceptors = new Interceptor[0][];
}
else
{
constructionInterceptors = new Interceptor[constructionInfos.length][];
for (int i = 0 ; i < constructionInfos.length ; i++)
{
constructionInterceptors[i] = constructionInfos[i].getInterceptors();
}
}
}
protected MethodByMethodData getMethodByMethodData()
{
if (methodByMethodData == null)
{
synchronized(lazyCollectionLock)
{
if (methodByMethodData == null)
{
methodByMethodData = new MethodByMethodData();
}
}
}
return methodByMethodData;
}
protected ConByMethodData getConByMethodData()
{
if (conByMethodData == null)
{
synchronized(lazyCollectionLock)
{
if (conByMethodData == null)
{
conByMethodData = new ConByMethodData();
}
}
}
return conByMethodData;
}
interface ResolveCallerConstuctorInfoAction
{
ConByMethodInfo resolveInfo(ConByMethodData data, long callingMethodHash, String calledClass, long calledConHash);
ResolveCallerConstuctorInfoAction PRIVILEGED = new ResolveCallerConstuctorInfoAction()
{
public ConByMethodInfo resolveInfo(final ConByMethodData data, final long callingMethodHash, final String calledClass, final long calledConHash)
{
try
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<ConByMethodInfo>()
{
public ConByMethodInfo run() throws Exception
{
return data.doResolveCallerConstructorInfo(callingMethodHash, calledClass, calledConHash);
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
throw new RuntimeException(ex);
}
}
};
ResolveCallerConstuctorInfoAction NON_PRIVILEGED = new ResolveCallerConstuctorInfoAction()
{
public ConByMethodInfo resolveInfo(ConByMethodData data, long callingMethodHash, String calledClass, long calledConHash)
{
return data.doResolveCallerConstructorInfo(callingMethodHash, calledClass, calledConHash);
}
};
}
interface ResolveCallerMethodInfoAction
{
MethodByMethodInfo resolveInfo(MethodByMethodData data, long callingMethodHash, String calledClass, long calledMethodHash);
ResolveCallerMethodInfoAction PRIVILEGED = new ResolveCallerMethodInfoAction()
{
public MethodByMethodInfo resolveInfo(final MethodByMethodData data, final long callingMethodHash, final String calledClass, final long calledMethodHash)
{
try
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodByMethodInfo>()
{
public MethodByMethodInfo run() throws Exception
{
return data.doResolveCallerMethodInfo(callingMethodHash, calledClass, calledMethodHash);
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
throw new RuntimeException(ex);
}
}
};
ResolveCallerMethodInfoAction NON_PRIVILEGED = new ResolveCallerMethodInfoAction()
{
public MethodByMethodInfo resolveInfo(MethodByMethodData data, long callingMethodHash, String calledClass, long calledMethodHash)
{
return data.doResolveCallerMethodInfo(callingMethodHash, calledClass, calledMethodHash);
}
};
}
interface ResolveConstructorCallerMethodInfoAction
{
MethodByConInfo resolveInfo(ClassAdvisor advisor, Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash);
ResolveConstructorCallerMethodInfoAction PRIVILEGED = new ResolveConstructorCallerMethodInfoAction()
{
public MethodByConInfo resolveInfo(final ClassAdvisor advisor, final Class<?> callingClass, final int callingIndex, final String calledClass, final long calledMethodHash)
{
try
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodByConInfo>()
{
public MethodByConInfo run() throws Exception
{
return advisor.doResolveConstructorCallerMethodInfo(callingClass, callingIndex, calledClass, calledMethodHash);
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
throw new RuntimeException(ex);
}
}
};
ResolveConstructorCallerMethodInfoAction NON_PRIVILEGED = new ResolveConstructorCallerMethodInfoAction()
{
public MethodByConInfo resolveInfo(ClassAdvisor advisor, Class<?> callingClass, int callingIndex, String calledClass, long calledMethodHash)
{
return advisor.doResolveConstructorCallerMethodInfo(callingClass, callingIndex, calledClass, calledMethodHash);
}
};
}
interface ResolveConstructorCallerConstructorInfoAction
{
ConByConInfo resolveInfo(ClassAdvisor advisor, Class<?> callingClass, int callingIndex, String calledClass, long calledConHash);
ResolveConstructorCallerConstructorInfoAction PRIVILEGED = new ResolveConstructorCallerConstructorInfoAction()
{
public ConByConInfo resolveInfo(final ClassAdvisor advisor, final Class<?> callingClass, final int callingIndex, final String calledClass, final long calledConHash)
{
try
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<ConByConInfo>()
{
public ConByConInfo run() throws Exception
{
return advisor.doResolveConstructorCallerConstructorInfo(callingClass, callingIndex, calledClass, calledConHash);
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
throw new RuntimeException(ex);
}
}
};
ResolveConstructorCallerConstructorInfoAction NON_PRIVILEGED = new ResolveConstructorCallerConstructorInfoAction()
{
public ConByConInfo resolveInfo(ClassAdvisor advisor, Class<?> callingClass, int callingIndex, String calledClass, long calledConHash)
{
return advisor.doResolveConstructorCallerConstructorInfo(callingClass, callingIndex, calledClass, calledConHash);
}
};
}
interface RebuildInterceptorsAction
{
void rebuildInterceptors(ClassAdvisor advisor);
void rebuildInterceptorsForAddedBinding(ClassAdvisor advisor, AdviceBinding binding);
void rebuildInterceptorsForRemovedBinding(ClassAdvisor advisor, AdviceBinding binding);
RebuildInterceptorsAction PRIVILEGED = new RebuildInterceptorsAction()
{
public void rebuildInterceptors(final ClassAdvisor advisor)
{
try
{
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
{
public Object run()
{
advisor.doRebuildInterceptors();
return null;
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
public void rebuildInterceptorsForAddedBinding(final ClassAdvisor advisor, final AdviceBinding binding)
{
try
{
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
{
public Object run()
{
advisor.doRebuildInterceptorsForAddedBinding(binding);
return null;
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
public void rebuildInterceptorsForRemovedBinding(final ClassAdvisor advisor, final AdviceBinding binding)
{
try
{
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
{
public Object run()
{
advisor.doRebuildInterceptorsForRemovedBinding(binding);
return null;
}
});
}
catch (PrivilegedActionException e)
{
Exception ex = e.getException();
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
throw new RuntimeException(ex);
}
}
};
RebuildInterceptorsAction NON_PRIVILEGED = new RebuildInterceptorsAction()
{
public void rebuildInterceptors(ClassAdvisor advisor)
{
advisor.doRebuildInterceptors();
}
public void rebuildInterceptorsForAddedBinding(ClassAdvisor advisor, AdviceBinding binding)
{
advisor.doRebuildInterceptorsForAddedBinding(binding);
}
public void rebuildInterceptorsForRemovedBinding(ClassAdvisor advisor, AdviceBinding binding)
{
advisor.doRebuildInterceptorsForRemovedBinding(binding);
}
};
}
private class MethodByMethodData
{
private volatile TLongObjectHashMap methodCalledByMethodBindings = UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP;
private volatile HashMap<String, ArrayList<ArrayList<AdviceBinding>>> backrefMethodCalledByMethodBindings = UnmodifiableEmptyCollections.EMPTY_HASHMAP;
private volatile TLongObjectHashMap methodCalledByMethodInterceptors = UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP;
public TLongObjectHashMap getMethodCalledByMethodInterceptors()
{
//No need to initialise map here if it is empty
return methodCalledByMethodInterceptors;
}
public TLongObjectHashMap getMethodCalledByMethodBindings()
{
//No need to initialise map here if it is empty
return methodCalledByMethodBindings;
}
public void rebuildCallerInterceptors() throws Exception
{
//No need to initialise map here if it is empty
long[] callingKeys = methodCalledByMethodInterceptors.keys();
for (int i = 0; i < callingKeys.length; i++)
{
long callingHash = callingKeys[i];
HashMap<String, TLongObjectHashMap> calledClasses = (HashMap<String, TLongObjectHashMap>) methodCalledByMethodInterceptors.get(callingHash);
for (Map.Entry<String, TLongObjectHashMap> entry : calledClasses.entrySet())
{
String cname = entry.getKey();
TLongObjectHashMap calledMethods = entry.getValue();
long[] calledKeys = calledMethods.keys();
for (int j = 0; j < calledKeys.length; j++)
{
long calledHash = calledKeys[j];
ArrayList<AdviceBinding> bindings = getCallerBindings(callingHash, cname, calledHash);
Method calling = MethodHashing.findMethodByHash(clazz, callingHash);
bindCallerInterceptorChain(bindings, callingHash, cname, calledHash, calling);
}
}
}
}
public void removeCallerPointcut(AdviceBinding binding)
{
//No need to initialise map here if it is empty
ArrayList<ArrayList<AdviceBinding>> backrefs = backrefMethodCalledByMethodBindings.get(binding.getName());
if (backrefs == null) return;
for (int i = 0; i < backrefs.size(); i++)
{
ArrayList<AdviceBinding> ref = backrefs.get(i);
ref.remove(binding);
}
}
public MethodByMethodInfo resolveCallerMethodInfo(long callingMethodHash, String calledClass, long calledMethodHash)
{
//The main entry point for adding data to the maps, let us initialise them here.
if (methodCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP ||
backrefMethodCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_HASHMAP ||
methodCalledByMethodInterceptors == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
synchronized(lazyCollectionLock)
{
if (methodCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
methodCalledByMethodBindings = new TLongObjectHashMap();
}
if (backrefMethodCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_HASHMAP)
{
backrefMethodCalledByMethodBindings = new HashMap<String, ArrayList<ArrayList<AdviceBinding>>>();
}
if (methodCalledByMethodInterceptors == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
methodCalledByMethodInterceptors = new TLongObjectHashMap();
}
}
}
if (System.getSecurityManager() == null)
{
return ResolveCallerMethodInfoAction.NON_PRIVILEGED.resolveInfo(this, callingMethodHash, calledClass, calledMethodHash);
}
else
{
return ResolveCallerMethodInfoAction.PRIVILEGED.resolveInfo(this, callingMethodHash, calledClass, calledMethodHash);
}
}
private MethodByMethodInfo doResolveCallerMethodInfo(long callingMethodHash, String calledClass, long calledMethodHash)
{
//Called via resolveCallerMethodInfo, maps are initialised
try
{
Method callingMethod = MethodHashing.findMethodByHash(clazz, callingMethodHash);
if (callingMethod == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = getClassLoader().loadClass(calledClass);
Method calledMethod = MethodHashing.findMethodByHash(called, calledMethodHash);
if (calledMethod == null) throw new RuntimeException("Unable to figure out calledmethod of a caller pointcut");
boolean matched = false;
for (AdviceBinding binding : manager.getBindingCollection().getMethodCallBindings())
{
if (binding.getPointcut().matchesCall(ClassAdvisor.this, callingMethod, called, calledMethod))
{
addMethodCalledByMethodPointcut(callingMethodHash, calledClass, calledMethodHash, binding);
matched = true;
}
}
if (!matched) initializeEmptyCallerChain(callingMethodHash, calledClass, calledMethodHash);
MethodByMethodInfo info = initializeCallerInterceptorsMap(callingMethodHash, calledClass, calledMethodHash, callingMethod, calledMethod);
ArrayList<AdviceBinding> bindings = getCallerBindings(callingMethodHash, calledClass, calledMethodHash);
bindCallerInterceptorChain(bindings, callingMethodHash, calledClass, calledMethodHash, callingMethod);
return info;
}
catch (Exception x)
{
throw new RuntimeException(x);
}
}
private void addMethodCalledByMethodPointcut(long callingMethodHash, String calledClass, long calledMethodHash, AdviceBinding binding) throws Exception
{
//Called via resolveCallerMethodInfo, maps are initialised
if (AspectManager.verbose) logger.debug("method call matched binding " + binding.getPointcut().getExpr());
adviceBindings.add(binding);
binding.addAdvisor(ClassAdvisor.this);
HashMap<String, TLongObjectHashMap> callingMethod = (HashMap<String, TLongObjectHashMap>)methodCalledByMethodBindings.get(callingMethodHash);
if (callingMethod == null)
{
callingMethod = new HashMap<String, TLongObjectHashMap>();
methodCalledByMethodBindings.put(callingMethodHash, callingMethod);
}
TLongObjectHashMap classMap = callingMethod.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingMethod.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledMethodHash);
boolean createdBindings = false;
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledMethodHash, bindings);
createdBindings = true;
}
if (!bindings.contains(binding)) bindings.add(binding);
// this is so that we can undeploy a caller
ArrayList<ArrayList<AdviceBinding>> backrefs = backrefMethodCalledByMethodBindings.get(binding.getName());
if (backrefs == null)
{
backrefs = new ArrayList<ArrayList<AdviceBinding>>();
backrefMethodCalledByMethodBindings.put(binding.getName(), backrefs);
backrefs.add(bindings);
}
else if (createdBindings) backrefs.add(bindings);
}
private void initializeEmptyCallerChain(long callingMethodHash, String calledClass, long calledMethodHash) throws Exception
{
//Called via resolveCallerMethodInfo, maps are initialised
HashMap<String, TLongObjectHashMap> callingMethod = (HashMap<String, TLongObjectHashMap>) methodCalledByMethodBindings.get(callingMethodHash);
if (callingMethod == null)
{
callingMethod = new HashMap<String, TLongObjectHashMap>();
methodCalledByMethodBindings.put(callingMethodHash, callingMethod);
}
TLongObjectHashMap classMap = callingMethod.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingMethod.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledMethodHash);
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledMethodHash, bindings);
}
}
private MethodByMethodInfo initializeCallerInterceptorsMap(long callingMethodHash, String calledClass, long calledMethodHash, Method callingMethod, Method calledMethod) throws Exception
{
//Called via resolveCallerMethodInfo, maps are initialised
HashMap<String, TLongObjectHashMap> calledClassesMap = (HashMap<String, TLongObjectHashMap>) methodCalledByMethodInterceptors.get(callingMethodHash);
if (calledClassesMap == null)
{
calledClassesMap = new HashMap<String, TLongObjectHashMap>();
methodCalledByMethodInterceptors.put(callingMethodHash, calledClassesMap);
}
TLongObjectHashMap calledMethodsMap = calledClassesMap.get(calledClass);
if (calledMethodsMap == null)
{
calledMethodsMap = new TLongObjectHashMap();
calledClassesMap.put(calledClass, calledMethodsMap);
}
//The standard MethodCalledByXXXXInvocation class calls by reflection and needs access
calledMethod.setAccessible(true);
Class<?> calledClazz = getClassLoader().loadClass(calledClass);
MethodByMethodInfo info = new MethodByMethodInfo(ClassAdvisor.this, calledClazz, calledMethod, callingMethod, callingMethodHash, calledMethodHash, null);
calledMethodsMap.put(calledMethodHash, info);
return info;
}
private ArrayList<AdviceBinding> getCallerBindings(long callingHash, String cname, long calledHash)
{
try
{
Method callingMethod = MethodHashing.findMethodByHash(clazz, callingHash);
if (callingMethod == null) throw new RuntimeException("Unable to figure out calling method of a caller pointcut");
Class<?> called = getClassLoader().loadClass(cname);
Method calledMethod = MethodHashing.findMethodByHash(called, calledHash);
if (calledMethod == null) throw new RuntimeException("Unable to figure out calledmethod of a caller pointcut");
Collection<AdviceBinding> bindings = manager.getBindingCollection().
getMethodCallBindings();
ArrayList<AdviceBinding> result = new ArrayList<AdviceBinding>(bindings.size());
for(AdviceBinding ab : bindings)
{
if (ab.getPointcut().matchesCall(ClassAdvisor.this, callingMethod, called, calledMethod))
{
result.add(ab);
}
}
return result;
}
catch(Exception e)
{
logger.error("Error happened when getting callerBindings",e);
return new ArrayList<AdviceBinding>();
}
}
private void bindCallerInterceptorChain(ArrayList<AdviceBinding> bindings, long callingHash, String cname, long calledHash, Method calling)
{
//Called via resolveCallerMethodInfo, maps are initialised
MethodByMethodInfo info = getCallerMethodInfo(callingHash, cname, calledHash);
info.clear();
for (AdviceBinding binding : bindings)
{
if (BindingClassifier.isCall(binding))
{
pointcutResolved(info, binding, new MethodCalledByMethodJoinpoint(info.getCallingMethod(), info.getMethod()));
}
}
finalizeMethodCalledByMethodInterceptorChain(info);
}
private MethodByMethodInfo getCallerMethodInfo(long callingMethodHash, String calledClass, long calledMethodHash)
{
//Called via resolveCallerMethodInfo, maps are initialised
HashMap<String, TLongObjectHashMap> calledClasses = (HashMap<String, TLongObjectHashMap>) methodCalledByMethodInterceptors.get(callingMethodHash);
TLongObjectHashMap calledMethods = calledClasses.get(calledClass);
MethodByMethodInfo info = (MethodByMethodInfo) calledMethods.get(calledMethodHash);
return info;
}
public void cleanup()
{
methodCalledByMethodBindings.clear();
backrefMethodCalledByMethodBindings.clear();
methodCalledByMethodInterceptors.clear();
}
}
private class ConByMethodData
{
// constructor caller pointcut support for methods calling constructors only
private volatile TLongObjectHashMap conCalledByMethodBindings = UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP;
private volatile HashMap<String, ArrayList<ArrayList<AdviceBinding>>> backrefConCalledByMethodBindings = UnmodifiableEmptyCollections.EMPTY_HASHMAP;
private volatile TLongObjectHashMap conCalledByMethodInterceptors = UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP;
public TLongObjectHashMap getConCalledByMethodInterceptors()
{
return conCalledByMethodInterceptors;
}
public ArrayList<AdviceBinding> getConCalledByMethodBindings(long callingHash, String cname, long calledHash)
{
try
{
Method callingMethod = MethodHashing.findMethodByHash(clazz, callingHash);
if (callingMethod == null) throw new RuntimeException("Unable to figure out calling method of a constructor caller pointcut");
Class<?> called = getClassLoader().loadClass(cname);
Constructor<?> calledCon = MethodHashing.findConstructorByHash(called, calledHash);
if (calledCon == null) throw new RuntimeException("Unable to figure out calledcon of a constructor caller pointcut");
Collection<AdviceBinding> bindings = manager.getBindingCollection().
getConstructorCallBindings();
ArrayList<AdviceBinding> result = new ArrayList<AdviceBinding>(bindings.size());
for(AdviceBinding ab : bindings)
{
if (ab.getPointcut().matchesCall(ClassAdvisor.this, callingMethod, called, calledCon))
{
result.add(ab);
}
}
return result;
}
catch(Exception e)
{
logger.error("Error happened conCalledMethod bindings", e);
return new ArrayList<AdviceBinding>();
}
}
public void rebuildCallerInterceptors() throws Exception
{
long[] callingKeys = conCalledByMethodInterceptors.keys();
for (int i = 0; i < callingKeys.length; i++)
{
long callingHash = callingKeys[i];
HashMap<String, TLongObjectHashMap> calledClasses = (HashMap<String, TLongObjectHashMap>) conCalledByMethodInterceptors.get(callingHash);
for (Map.Entry<String, TLongObjectHashMap> entry : calledClasses.entrySet())
{
String cname = entry.getKey();
TLongObjectHashMap calledMethods = entry.getValue();
long[] calledKeys = calledMethods.keys();
for (int j = 0; j < calledKeys.length; j++)
{
long calledHash = calledKeys[j];
ArrayList<AdviceBinding> bindings = getConCalledByMethodBindings(callingHash, cname, calledHash);
bindConCalledByMethodInterceptorChain(bindings, callingHash, cname, calledHash);
}
}
}
}
public ConByMethodInfo resolveCallerConstructorInfo(long callingMethodHash, String calledClass, long calledConHash)
{
//The main entry point for adding data to the maps, let us initialise them here.
if (conCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP ||
backrefConCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_HASHMAP ||
conCalledByMethodInterceptors == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
synchronized(lazyCollectionLock)
{
if (conCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
conCalledByMethodBindings = new TLongObjectHashMap();
}
if (backrefConCalledByMethodBindings == UnmodifiableEmptyCollections.EMPTY_HASHMAP)
{
backrefConCalledByMethodBindings = new HashMap<String, ArrayList<ArrayList<AdviceBinding>>>();
}
if (conCalledByMethodInterceptors == UnmodifiableEmptyCollections.EMPTY_TLONG_OBJECT_HASHMAP)
{
conCalledByMethodInterceptors = new TLongObjectHashMap();
}
}
}
if (System.getSecurityManager() == null)
{
return ResolveCallerConstuctorInfoAction.NON_PRIVILEGED.resolveInfo(this, callingMethodHash, calledClass, calledConHash);
}
else
{
return ResolveCallerConstuctorInfoAction.PRIVILEGED.resolveInfo(this, callingMethodHash, calledClass, calledConHash);
}
}
private ConByMethodInfo doResolveCallerConstructorInfo(long callingMethodHash, String calledClass, long calledConHash)
{
try
{
Method callingMethod = MethodHashing.findMethodByHash(clazz, callingMethodHash);
if (callingMethod == null) throw new RuntimeException("Unable to figure out calling method of a constructor caller pointcut");
Class<?> called = getClassLoader().loadClass(calledClass);
Constructor<?> calledCon = MethodHashing.findConstructorByHash(called, calledConHash);
if (calledCon == null) throw new RuntimeException("Unable to figure out calledcon of a constructor caller pointcut");
boolean matched = false;
ClassifiedBindingAndPointcutCollection bindingCol = manager.getBindingCollection();
for (AdviceBinding binding : bindingCol.getConstructorCallBindings())
{
if (binding.getPointcut().matchesCall(ClassAdvisor.this, callingMethod, called, calledCon))
{
addConstructorCalledByMethodPointcut(callingMethodHash, calledClass, calledConHash, binding);
matched = true;
}
}
if (!matched) initializeConCalledByMethodEmptyChain(callingMethodHash, calledClass, calledConHash);
ConByMethodInfo info = initializeConCalledByMethodInterceptorsMap(callingMethod, callingMethodHash, calledClass, calledConHash, calledCon);
ArrayList<AdviceBinding> bindings = getConCalledByMethodBindings(callingMethodHash, calledClass, calledConHash);
bindConCalledByMethodInterceptorChain(bindings, callingMethodHash, calledClass, calledConHash);
return info;
}
catch (Exception x)
{
throw new RuntimeException(x);
}
}
private void addConstructorCalledByMethodPointcut(long callingMethodHash, String calledClass, long calledMethodHash, AdviceBinding binding) throws Exception
{
if (AspectManager.verbose) logger.debug("method call matched binding " + binding.getPointcut().getExpr());
adviceBindings.add(binding);
binding.addAdvisor(ClassAdvisor.this);
HashMap<String, TLongObjectHashMap> callingMethod = (HashMap<String, TLongObjectHashMap>) conCalledByMethodBindings.get(callingMethodHash);
if (callingMethod == null)
{
callingMethod = new HashMap<String, TLongObjectHashMap>();
conCalledByMethodBindings.put(callingMethodHash, callingMethod);
}
TLongObjectHashMap classMap = callingMethod.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingMethod.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledMethodHash);
boolean createdBindings = false;
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledMethodHash, bindings);
createdBindings = true;
}
if (!bindings.contains(binding)) bindings.add(binding);
// this is so that we can undeploy a caller
ArrayList<ArrayList<AdviceBinding>> backrefs = backrefConCalledByMethodBindings.get(binding.getName());
if (backrefs == null)
{
backrefs = new ArrayList<ArrayList<AdviceBinding>>();
backrefConCalledByMethodBindings.put(binding.getName(), backrefs);
backrefs.add(bindings);
}
else if (createdBindings) backrefs.add(bindings);
}
private ConByMethodInfo initializeConCalledByMethodInterceptorsMap(Method callingMethod, long callingMethodHash, String calledClass, long calledConHash, Constructor<?> calledCon) throws Exception
{
HashMap<String, TLongObjectHashMap> calledClassesMap = (HashMap<String, TLongObjectHashMap>) conCalledByMethodInterceptors.get(callingMethodHash);
if (calledClassesMap == null)
{
calledClassesMap = new HashMap<String, TLongObjectHashMap>();
conCalledByMethodInterceptors.put(callingMethodHash, calledClassesMap);
}
TLongObjectHashMap calledMethodsMap = calledClassesMap.get(calledClass);
if (calledMethodsMap == null)
{
calledMethodsMap = new TLongObjectHashMap();
calledClassesMap.put(calledClass, calledMethodsMap);
}
ConByMethodInfo info = createConByMethodInfo(calledClass, callingMethod, callingMethodHash, calledCon, calledConHash);
calledMethodsMap.put(calledConHash, info);
return info;
}
private void initializeConCalledByMethodEmptyChain(long callingMethodHash, String calledClass, long calledConHash) throws Exception
{
HashMap<String, TLongObjectHashMap> callingMethod = (HashMap<String, TLongObjectHashMap>) conCalledByMethodBindings.get(callingMethodHash);
if (callingMethod == null)
{
callingMethod = new HashMap<String, TLongObjectHashMap>();
conCalledByMethodBindings.put(callingMethodHash, callingMethod);
}
TLongObjectHashMap classMap = callingMethod.get(calledClass);
if (classMap == null)
{
classMap = new TLongObjectHashMap();
callingMethod.put(calledClass, classMap);
}
ArrayList<AdviceBinding> bindings = (ArrayList<AdviceBinding>) classMap.get(calledConHash);
if (bindings == null)
{
bindings = new ArrayList<AdviceBinding>();
classMap.put(calledConHash, bindings);
}
}
private ConByMethodInfo getConCalledByMethod(long callingMethodHash, String calledClass, long calledConHash)
{
HashMap<String, TLongObjectHashMap> calledClasses = (HashMap<String, TLongObjectHashMap>) conCalledByMethodInterceptors.get(callingMethodHash);
TLongObjectHashMap calledMethods = calledClasses.get(calledClass);
ConByMethodInfo info = (ConByMethodInfo) calledMethods.get(calledConHash);
return info;
}
private ConByMethodInfo createConByMethodInfo(String calledClass, Method callingMethod, long callingMethodHash, Constructor<?> calledCon, long calledConHash) throws Exception
{
//The standard ConstructorCalledByXXXXInvocation class calls by reflection and needs access
calledCon.setAccessible(true);
Class<?> calledClazz = getClassLoader().loadClass(calledClass);
try
{
int index = calledClass.lastIndexOf('.');
String baseClassName = calledClass.substring(index + 1);
Method wrapper = calledCon.getDeclaringClass().getDeclaredMethod(ConstructorExecutionTransformer.constructorFactory(baseClassName), calledCon.getParameterTypes());
return new ConByMethodInfo(ClassAdvisor.this, calledClazz, callingMethod, callingMethodHash, calledCon, calledConHash, wrapper, null);
}
catch (NoSuchMethodException e)
{
return new ConByMethodInfo(ClassAdvisor.this, calledClazz, callingMethod, callingMethodHash, calledCon, calledConHash, null, null);
}
}
private void bindConCalledByMethodInterceptorChain(ArrayList<AdviceBinding> bindings, long callingHash, String cname, long calledHash) throws Exception
{
ConByMethodInfo info = getConCalledByMethod(callingHash, cname, calledHash);
info.clear();
for (AdviceBinding binding : bindings)
{
if (BindingClassifier.isConstructorCall(binding))
{
pointcutResolved(info, binding, new ConstructorCalledByMethodJoinpoint(info.getCallingMethod(), info.getConstructor()));
}
}
finalizeConCalledByMethodInterceptorChain(info);
}
public void cleanup()
{
conCalledByMethodBindings.clear();
conCalledByMethodInterceptors.clear();
}
}
}