/**************************************************************************************
* 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.container;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
import org.codehaus.aspectwerkz.transform.TransformationConstants;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.aspectwerkz.util.Strings;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Create a factory class for different aspect life cycle models.
* <p/>
* Each model ends in instantiating the aspect thru "new XAspect()" if there is no aspect container, or thru
* keeping a reference to the container and delegating to it thru container.aspectOf(..).
* The container itself is created with a one per factory basis (thus controlled by QName) thru
* "new XContainer()" or "new XContainer(aspectClass, aopSystemClassLoader, String uuid, String aspectQName, Map aspectParam)"
* <p/>
* Each model has the aspectOf(..) method, and the "hasAspect(..)" method.
* <p/>
* PerObject has a bind(object) method suitable for perTarget / perThis, that delegates to a supposely implemented
* interface in the target object itself (to avoid a map)
* <p/>
* PerCflowX has a bind(thread) / unbind(thread) suitable for perCflow / perCflowBelow.
* <p/>
* TODO: none is synchronized. AspectJ does not synchronize neither...
*
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public abstract class AbstractAspectFactoryCompiler implements Opcodes, TransformationConstants {
private static final boolean DUMP_JIT_FACTORIES = AspectWerkzPreProcessor.DUMP_AFTER;
final static Artifact[] EMPTY_ARTIFACT_ARRAY = new Artifact[0];
private final ClassLoader m_loader;
private final String m_uuid;
protected final String m_aspectQualifiedName;
protected final String m_aspectClassName;
protected final String m_aspectClassSignature;
protected final String m_aspectFactoryClassName;
protected final String m_aspectContainerClassName;
protected final boolean m_hasAspectContainer;
private boolean m_hasParameters;
private String m_rawParameters;
private Map m_parametersLazy;
protected ClassWriter m_cw;
protected MethodVisitor m_clinit;
public AbstractAspectFactoryCompiler(String uuid,
String aspectClassName,
String aspectQualifiedName,
String containerClassName,
String rawParameters,
ClassLoader loader) {
m_uuid = uuid;
m_aspectClassName = aspectClassName.replace('.', '/');
m_aspectClassSignature = 'L' + m_aspectClassName + ';';
m_aspectQualifiedName = aspectQualifiedName;
m_aspectFactoryClassName = AspectFactoryManager.getAspectFactoryClassName(
m_aspectClassName, m_aspectQualifiedName
);
if (containerClassName != null) {
m_aspectContainerClassName = containerClassName.replace('.', '/');
m_hasAspectContainer = true;
} else {
m_aspectContainerClassName = null;
m_hasAspectContainer = false;
}
if (rawParameters != null) {
m_rawParameters = rawParameters;
m_hasParameters = true;
} else {
m_rawParameters = null;
m_hasParameters = false;
}
m_loader = loader;
}
private Map getParameters() {
if (m_parametersLazy == null) {
Map map;
if (m_rawParameters != null) {
map = new HashMap();
String[] raw = Strings.splitString(m_rawParameters, DELIMITER);
for (int i = 0; i < raw.length; i++) {
if (i < raw.length) {
map.put(raw[i], raw[++i]);
}
}
} else {
map = new HashMap(0);
}
m_parametersLazy = map;
}
return m_parametersLazy;
}
public Artifact compile() {
m_cw = AsmHelper.newClassWriter(true);
m_cw.visit(
AsmHelper.JAVA_VERSION,
ACC_PUBLIC,
m_aspectFactoryClassName,
null,
OBJECT_CLASS_NAME,
null
);
// create a CLASS field to host this factory class
m_cw.visitField(
ACC_STATIC + ACC_PRIVATE,
FACTORY_CLASS_FIELD_NAME,
CLASS_CLASS_SIGNATURE,
null,
null
);
// create a clinit method
m_clinit = m_cw.visitMethod(
ACC_STATIC,
CLINIT_METHOD_NAME,
CLINIT_METHOD_SIGNATURE,
null,
null
);
// init the CLASS field
m_clinit.visitLdcInsn(m_aspectFactoryClassName.replace('/', '.'));
m_clinit.visitMethodInsn(
INVOKESTATIC,
CLASS_CLASS,
FOR_NAME_METHOD_NAME,
FOR_NAME_METHOD_SIGNATURE
);
m_clinit.visitFieldInsn(
PUTSTATIC,
m_aspectFactoryClassName,
FACTORY_CLASS_FIELD_NAME,
CLASS_CLASS_SIGNATURE
);
if (m_hasParameters) {
createParametersFieldAndClinit();
}
if (m_hasAspectContainer) {
createAspectContainerFieldAndClinit();
}
createAspectOf();
createHasAspect();
createOtherArtifacts();
m_clinit.visitInsn(RETURN);
m_clinit.visitMaxs(0, 0);
Artifact artifact = new Artifact(m_aspectFactoryClassName, m_cw.toByteArray());
if (DUMP_JIT_FACTORIES) {
if (AspectWerkzPreProcessor.DUMP_PATTERN.matches(artifact.className.replace('/', '.'))) {
try {
AsmHelper.dumpClass(AspectWerkzPreProcessor.DUMP_DIR_AFTER, artifact.className, artifact.bytecode);
} catch (IOException e) {
;//TODO report a warning
}
}
}
return artifact;
}
protected abstract void createAspectOf();
protected abstract void createHasAspect();
protected abstract void createOtherArtifacts();
private void createParametersFieldAndClinit() {
m_cw.visitField(
ACC_PRIVATE + ACC_STATIC + ACC_FINAL,
FACTORY_PARAMS_FIELD_NAME,
MAP_CLASS_SIGNATURE,
null,
null
);
m_clinit.visitTypeInsn(NEW, HASH_MAP_CLASS_NAME);
m_clinit.visitInsn(DUP);
m_clinit.visitMethodInsn(INVOKESPECIAL, HASH_MAP_CLASS_NAME, INIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE);
for (Iterator iterator = getParameters().entrySet().iterator(); iterator.hasNext();) {
m_clinit.visitInsn(DUP);
Map.Entry entry = (Map.Entry) iterator.next();
m_clinit.visitLdcInsn(entry.getKey());
m_clinit.visitLdcInsn(entry.getValue());
m_clinit.visitMethodInsn(INVOKEINTERFACE, MAP_CLASS_NAME, PUT_METHOD_NAME, PUT_METHOD_SIGNATURE);
m_clinit.visitInsn(POP);
}
m_clinit.visitFieldInsn(PUTSTATIC, m_aspectFactoryClassName, FACTORY_PARAMS_FIELD_NAME, MAP_CLASS_SIGNATURE);
}
private void createAspectContainerFieldAndClinit() {
m_cw.visitField(
ACC_PRIVATE + ACC_STATIC,
FACTORY_CONTAINER_FIELD_NAME,
ASPECT_CONTAINER_CLASS_SIGNATURE,
null,
null
);
//support 2 different ctor for AspectContainer impl.
ClassInfo containerClassInfo = AsmClassInfo.getClassInfo(
m_aspectContainerClassName,
m_loader
);
boolean hasConstructor = false;
for (int i = 0; i < containerClassInfo.getConstructors().length; i++) {
ConstructorInfo constructorInfo = containerClassInfo.getConstructors()[i];
if (ASPECT_CONTAINER_OPTIONAL_INIT_SIGNATURE.equals(constructorInfo.getSignature())) {
hasConstructor = true;
break;
}
//TODO: check for no-arg ctor to avoid verify error and report error
}
m_clinit.visitTypeInsn(NEW, m_aspectContainerClassName);
m_clinit.visitInsn(DUP);
if (hasConstructor) {
m_clinit.visitLdcInsn(m_aspectClassName.replace('/', '.'));
m_clinit.visitMethodInsn(INVOKESTATIC, CLASS_CLASS, FOR_NAME_METHOD_NAME, FOR_NAME_METHOD_SIGNATURE);
//Note have access to the CL that defines the aspect (can be child of aspect CL)
m_clinit.visitFieldInsn(
GETSTATIC, m_aspectFactoryClassName, FACTORY_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE
);
m_clinit.visitMethodInsn(
INVOKEVIRTUAL,
CLASS_CLASS,
GETCLASSLOADER_METHOD_NAME,
CLASS_CLASS_GETCLASSLOADER_METHOD_SIGNATURE
);
m_clinit.visitLdcInsn(m_uuid);
m_clinit.visitLdcInsn(m_aspectQualifiedName);
if (m_hasParameters) {
m_clinit.visitFieldInsn(
GETSTATIC, m_aspectFactoryClassName, FACTORY_PARAMS_FIELD_NAME, MAP_CLASS_SIGNATURE
);
} else {
m_clinit.visitInsn(ACONST_NULL);
}
m_clinit.visitMethodInsn(
INVOKESPECIAL,
m_aspectContainerClassName,
INIT_METHOD_NAME,
ASPECT_CONTAINER_OPTIONAL_INIT_SIGNATURE
);
} else {
m_clinit.visitMethodInsn(
INVOKESPECIAL, m_aspectContainerClassName, INIT_METHOD_NAME, NO_PARAM_RETURN_VOID_SIGNATURE
);
}
m_clinit.visitFieldInsn(
PUTSTATIC, m_aspectFactoryClassName, FACTORY_CONTAINER_FIELD_NAME, ASPECT_CONTAINER_CLASS_SIGNATURE
);
}
}