/*
* 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 java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import org.jboss.aop.advice.AdviceBinding;
import org.jboss.aop.metadata.ClassMetaDataBinding;
import org.jboss.aop.metadata.ClassMetaDataLoader;
import org.jboss.aop.util.Advisable;
import org.jboss.aop.util.ConstructorComparator;
import org.jboss.aop.util.FieldComparator;
import org.jboss.aop.util.MethodHashing;
import org.jboss.aop.util.logging.AOPLogger;
import org.jboss.logging.Logger;
/**
* Comment
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @version $Revision: 68509 $
*/
public class ClassContainer extends Advisor
{
private static final Logger logger = AOPLogger.getLogger(ClassContainer.class);
private boolean chainOverridingForInheritedMethods;
public ClassContainer(String name, AspectManager manager)
{
super(name, manager);
}
public void initializeClassContainer()
{
initializeMetadata();
rebuildInterceptors();
}
public void setClass(Class clazz)
{
this.clazz = clazz;
}
public void initializeMetadata()
{
createMethodMap();
createConstructorTables();
createFieldTable();
getManager().attachMetaData(this, clazz);
rebindClassMetaData();
deployAnnotationOverrides();
}
protected Field[] advisedFields;
private void populateFieldTable(ArrayList<Field> fields, final Class superclass)
{
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 = AccessController.doPrivileged(new PrivilegedAction<Field[]>()
{
public Field[] run()
{
return superclass.getDeclaredFields();
}
});
for (int i = 0; i < declaredFields.length; i++)
{
if (Advisable.isAdvisable(declaredFields[i]))
{
temp.add(declaredFields[i]);
}
}
Collections.sort(temp, FieldComparator.INSTANCE);
fields.addAll(temp);
}
/**
* Gets advised methods.
*/
protected void createFieldTable()
{
ArrayList<Field> fields = new ArrayList<Field>();
populateFieldTable(fields, clazz);
advisedFields = fields.toArray(new Field[fields.size()]);
}
protected void rebuildInterceptors()
{
adviceBindings.clear();
if (this.constructorInfos == null)
{
createInterceptorChains();
}
else
{
updateInterceptorChains();
}
}
public 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 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();
}
}
protected void bindClassMetaData(ClassMetaDataBinding data)
{
try
{
ClassMetaDataLoader loader = data.getLoader();
Object[] objs = advisedMethods.getValues();
Method[] methods = new Method[objs.length];
for (int i = 0; i < objs.length; i++) methods[i] = (Method) objs[i];
loader.bind(this, data, methods, advisedFields, clazz.getDeclaredConstructors());
}
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 = (ClassMetaDataBinding) classMetaDataBindings.get(i);
bindClassMetaData(data);
}
}
protected void createMethodMap()
{
initAdvisedMethodsMap();
try
{
Method[] declaredMethods = clazz.getMethods();
for (int i = 0; i < declaredMethods.length; i++)
{
if (ClassAdvisor.isAdvisable(declaredMethods[i]))
{
long hash = MethodHashing.methodHash(declaredMethods[i]);
advisedMethods.put(hash, declaredMethods[i]);
}
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
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);
info.setUnadvisedMethod(amethod);
info.setHash(keys[i]);
info.setAdvisor(this);
methodInfos.put(keys[i], info);
}
}
protected void createConstructorTables()
{
constructors = SecurityActions.getDeclaredConstructors(clazz);
if (constructors.length > 0)
{
for (int i = 0; i < constructors.length; i++)
{
SecurityActions.setAccessible(constructors[i]);
}
Arrays.sort(constructors, ConstructorComparator.INSTANCE);
}
}
protected void createInterceptorChains()
{
initializeMethodChain();
initializeConstructorChain();
LinkedHashMap bindings = manager.getBindings();
synchronized (bindings)
{
if (bindings.size() > 0)
{
Iterator it = bindings.values().iterator();
while (it.hasNext())
{
AdviceBinding binding = (AdviceBinding) it.next();
if (AspectManager.verbose && logger.isDebugEnabled()) logger.debug("iterate binding " + binding.getName());
resolveMethodPointcut(binding);
resolveConstructorPointcut(binding);
}
}
}
finalizeChain(constructorInfos);
finalizeMethodChain();
populateInterceptorsFromInfos();
doesHaveAspects = adviceBindings.size() > 0;
}
protected void updateInterceptorChains()
{
resetChain(this.methodInfos);
resetChain(this.constructorInfos);
LinkedHashMap bindings = manager.getBindings();
synchronized (bindings)
{
if (bindings.size() > 0)
{
Iterator it = bindings.values().iterator();
while (it.hasNext())
{
AdviceBinding binding = (AdviceBinding) it.next();
if (AspectManager.verbose && logger.isDebugEnabled()) logger.debug("iterate binding " + binding.getName());
resolveMethodPointcut(binding);
resolveConstructorPointcut(binding);
}
}
}
finalizeChain(constructorInfos);
finalizeMethodChain();
populateInterceptorsFromInfos();
doesHaveAspects = adviceBindings.size() > 0;
}
/**
* @return the value of chainOverridingForInheritedMethods
* @see Advisor#chainOverridingForInheritedMethods()
*/
@Override
public boolean chainOverridingForInheritedMethods()
{
return chainOverridingForInheritedMethods;
}
/**
* @param overriding the new value of chainOverridingForInheritedMethods
* @see Advisor#chainOverridingForInheritedMethods()
*/
protected void setChainOverridingForInheritedMethods(boolean overriding)
{
this.chainOverridingForInheritedMethods = overriding;
}
}