/************************************************************************************** * 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 ); } }