/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.aspect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.codehaus.aspectwerkz.ContextClassLoader;
import org.codehaus.aspectwerkz.DeploymentModel;
import org.codehaus.aspectwerkz.Mixin;
import org.codehaus.aspectwerkz.CrossCuttingInfo;
import org.codehaus.aspectwerkz.definition.IntroductionDefinition;
import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
/**
* Interface+Implementation Introduction
* <p/>
* This represents the inner class mixin based implementation in the system
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
*/
public class Introduction implements Mixin {
private static final int MIXIN_CONSTRUCTION_TYPE_UNKNOWN = 0;
private static final int MIXIN_CONSTRUCTION_TYPE_DEFAULT = 1;
private static final int MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO = 2;
private static final Object[] ARRAY_WITH_CROSS_CUTTING_INFO = new Object[1];
/**
* An empty <code>Object</code> array.
*/
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[]{};
/**
* Mixin name
*/
private String m_name;
/**
* Mixin implementation as aspect inner class Note: when swapped the impl can be an autonomous class
*/
private Class m_mixinImplClass;
/**
* Mixin implementation as aspect inner class Note: when swapped the impl can be an autonomous class
*/
private Object m_mixinImpl;
/**
* The constructor for the mixin.
*/
private Constructor m_mixinConstructor;
/**
* The container for the introduction (single per JVM)
*/
private IntroductionContainer m_container;
/**
* The cross-cutting info for the mixin.
*/
private CrossCuttingInfo m_crossCuttingInfo;
/**
* Defintion to which this mixin relates
*/
private IntroductionDefinition m_definition;
/**
* Holds the deployment model. The deployment model of an introduction is tight to the aspect deployment model that
* defines it
*/
protected int m_deploymentModel;
/**
* The mixin construction type.
*/
private int m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_UNKNOWN;
/**
* Create a new introduction
*
* @param name of this introduction - by convention the AspectClassFQN $ InnerClass
* @param implClass
* @param crossCuttingInfo which defines this mixin
* @param definition
*/
public Introduction(
final String name,
final Class implClass,
final CrossCuttingInfo crossCuttingInfo,
final IntroductionDefinition definition) {
m_name = name;
m_crossCuttingInfo = crossCuttingInfo;
m_definition = definition;
m_mixinImplClass = implClass;
m_mixinConstructor = findConstructor();
ARRAY_WITH_CROSS_CUTTING_INFO[0] = m_crossCuttingInfo;
// handle deploymentModel dependancies
// defaults to Aspect deploymentModel
// else supported models are:
// Mixin Aspect
// perJVM perJVM
// perClass perJVM,perClass
// perInstance perJVM,perClass,perInstance
// perThread perThread
// todo all those checks should be done earlier
// (AspectC thought doclet inheritance might cause problem when inheritating compiled aspects without source code)
if (definition.getDeploymentModel() == null) {
m_deploymentModel = m_crossCuttingInfo.getDeploymentModel();
}
else {
int model = DeploymentModel.getDeploymentModelAsInt(definition.getDeploymentModel());
if (DeploymentModel.isMixinDeploymentModelCompatible(model, m_crossCuttingInfo.getDeploymentModel())) {
m_deploymentModel = model;
}
else {
throw new RuntimeException(
"could no create mixin from aspect: incompatible deployment models : mixin " +
DeploymentModel.getDeploymentModelAsString(model) +
" with aspect " +
DeploymentModel.getDeploymentModelAsString(m_crossCuttingInfo.getDeploymentModel())
);
}
}
m_mixinImpl = createMixin();
// try {
// if (isInnerClassOf(implClass, m_crossCuttingInfo.getAspectClass())) {
// Constructor constructor = m_mixinImplClass.getConstructors()[0];
// if (constructor.getParameterTypes().length == 0) {
// // static inner class
// m_mixinImpl = m_mixinImplClass.newInstance();
// }
// else {
// // member inner class
// constructor.setAccessible(true);
// m_mixinImpl = constructor.newInstance(new Object[]{crossCuttingInfo});
// }
// }
// else {
// m_mixinImpl = m_mixinImplClass.newInstance();
// }
// }
// catch (Exception e) {
// throw new RuntimeException(
// "could no create mixin from aspect [be sure to have a public Mixin impl as inner class]: " +
// e.toString()
// );
// }
}
/**
* Clone the prototype Introduction.
*
* @param prototype introduction
* @param crossCuttingInfo the cross-cutting info
* @return new introduction instance
*/
public static Introduction newInstance(final Introduction prototype, final CrossCuttingInfo crossCuttingInfo) {
return new Introduction(
prototype.m_name, prototype.m_mixinImplClass, crossCuttingInfo, prototype.m_definition
);
}
/**
* Set the container.
*
* @param container
*/
public void setContainer(final IntroductionContainer container) {
m_container = container;
}
/**
* Returns the cross-cutting info.
*
* @return the cross-cutting info.
*/
public CrossCuttingInfo getCrossCuttingInfo() {
return m_crossCuttingInfo;
}
/**
* Returns the definition.
*
* @return definition related to this introduction
*/
public IntroductionDefinition getIntroductionDefinition() {
return m_definition;
}
/**
* Returns the name of the mixin.
*
* @return the name
*/
public String getName() {
return m_name;
}
/**
* Returns the mixin deployment model.
*
* @return the deployment model
*/
public int getDeploymentModel() {
return m_deploymentModel;
}
/**
* Sets the deployment model.
*
* @param deploymentModel the deployment model
*/
public void setDeploymentModel(final int deploymentModel) {
m_deploymentModel = deploymentModel;
}
/**
* Invokes the method with the index specified. Invoked by methods without any parameters (slight performance gain
* since we are saving us one array creation).
*
* @param methodIndex the method index
* @param callingObject a reference to the calling object
* @return the result from the invocation
*/
public Object invokeMixin(final int methodIndex, final Object callingObject) throws Throwable {
return invokeMixin(methodIndex, EMPTY_OBJECT_ARRAY, callingObject);
}
/**
* Invokes an introduced method with the index specified.
*
* @param methodIndex the method index
* @param parameters the parameters for the invocation
* @param callingObject a reference to the calling object
* @return the result from the invocation
*/
public Object invokeMixin(final int methodIndex, final Object[] parameters, final Object callingObject)
throws Throwable{
Object result = null;
switch (m_deploymentModel) {
case DeploymentModel.PER_JVM:
result = m_container.invokeIntroductionPerJvm(methodIndex, parameters);
break;
case DeploymentModel.PER_CLASS:
result = m_container.invokeIntroductionPerClass(callingObject, methodIndex, parameters);
break;
case DeploymentModel.PER_INSTANCE:
result = m_container.invokeIntroductionPerInstance(callingObject, methodIndex, parameters);
break;
case DeploymentModel.PER_THREAD:
result = m_container.invokeIntroductionPerThread(methodIndex, parameters);
break;
default:
throw new RuntimeException(
"invalid deployment model: " +
m_crossCuttingInfo.getDeploymentModel()
);
}
return result;
}
/**
* Returns the implementation class name for the mixin.
*
* @return the implementation class name for the mixin
*/
public String getImplementationClassName() {
return m_mixinImpl.getClass().getName();
}
/**
* Returns the implementation object for the mixin.
*
* @return the implementation for the mixin
*/
public Object getImplementation() {
return m_mixinImpl;
}
/**
* Swaps the current introduction implementation.
*
* @param className the class name of the new implementation
*/
public void swapImplementation(final String className) {
if (className == null) {
throw new IllegalArgumentException("class name can not be null");
}
try {
Class newImplClass = ContextClassLoader.loadClass(className);//todo pbly old impl.getClassLoader() would be safer
m_container.swapImplementation(newImplClass);
}
catch (Exception e) {
throw new WrappedRuntimeException(e);
}
}
/**
* Check if klazz is an inner class of containingClass. Based on name to support private inner class with no arg
* ctor.
*
* @param klazz
* @param containingClass
* @return true if is an inner class
*/
private static boolean isInnerClassOf(final Class klazz, final Class containingClass) {
return klazz.getName().startsWith(containingClass.getName() + "$");
}
/**
* Grabs the correct constructor for the mixin.
*
* @return the constructor for the mixin
*/
private Constructor findConstructor() {
Constructor mixinConstructor = null;
Constructor[] constructors = m_mixinImplClass.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
Class[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == 0) {
m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_DEFAULT;
mixinConstructor = constructor;
}
else if (parameterTypes.length == 1 && parameterTypes[0].equals(CrossCuttingInfo.class)) {
m_mixinConstructionType = MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO;
mixinConstructor = constructor;
break;
}
}
if (m_mixinConstructionType == MIXIN_CONSTRUCTION_TYPE_UNKNOWN) {
throw new RuntimeException(
"mixin [" + m_mixinImplClass.getName() +
"] does not have a valid constructor (either default no-arg or one that takes a CrossCuttingInfo type as its only parameter)"
);
}
return mixinConstructor;
}
/**
* Creates a new mixin instance.
*
* @return the new mixin instance
*/
private Object createMixin() {
try {
switch (m_mixinConstructionType) {
case MIXIN_CONSTRUCTION_TYPE_DEFAULT:
return m_mixinConstructor.newInstance(EMPTY_OBJECT_ARRAY);
case MIXIN_CONSTRUCTION_TYPE_CROSS_CUTTING_INFO:
return m_mixinConstructor.newInstance(ARRAY_WITH_CROSS_CUTTING_INFO);
default:
throw new RuntimeException(
"mixin [" + m_mixinImplClass.getName() +
"] does not have a valid constructor (either default no-arg or one that takes a CrossCuttingInfo type as its only parameter)"
);
}
}
catch (InstantiationException e) {
throw new WrappedRuntimeException(e);
}
catch (IllegalAccessException e) {
throw new WrappedRuntimeException(e);
}
catch (InvocationTargetException e) {
throw new WrappedRuntimeException(e.getTargetException());
}
}
/**
* Swap the implementation of the mixin represented by this Introduction wrapper.
*
* @param newImplClass
*/
void swapImplementation(final Class newImplClass) {
m_mixinImplClass = newImplClass;
m_mixinConstructor = findConstructor();
m_mixinImpl = createMixin();
}
}