001package io.ebean.enhance.transactional; 002 003import io.ebean.enhance.asm.AnnotationVisitor; 004import io.ebean.enhance.asm.ClassVisitor; 005import io.ebean.enhance.asm.MethodVisitor; 006import io.ebean.enhance.asm.Opcodes; 007import io.ebean.enhance.common.AlreadyEnhancedException; 008import io.ebean.enhance.common.AnnotationInfo; 009import io.ebean.enhance.common.AnnotationInfoVisitor; 010import io.ebean.enhance.common.ClassMeta; 011import io.ebean.enhance.common.EnhanceConstants; 012import io.ebean.enhance.common.EnhanceContext; 013 014import java.util.ArrayList; 015import java.util.logging.Level; 016import java.util.logging.Logger; 017 018/** 019 * ClassAdapter used to add transactional support. 020 */ 021public class ClassAdapterTransactional extends ClassVisitor { 022 023 private static final Logger logger = Logger.getLogger(ClassAdapterTransactional.class.getName()); 024 025 private final ArrayList<String> transactionalMethods = new ArrayList<>(); 026 027 private final EnhanceContext enhanceContext; 028 029 private final ClassLoader classLoader; 030 031 private ArrayList<ClassMeta> transactionalInterfaces = new ArrayList<>(); 032 033 /** 034 * Class level annotation information. 035 */ 036 private AnnotationInfo classAnnotationInfo; 037 038 private String className; 039 040 public ClassAdapterTransactional(ClassVisitor cv, ClassLoader classLoader, EnhanceContext context) { 041 super(Opcodes.ASM6, cv); 042 this.classLoader = classLoader; 043 this.enhanceContext = context; 044 } 045 046 public String className() { 047 return className; 048 } 049 050 public boolean isLog(int level) { 051 return enhanceContext.isLog(level); 052 } 053 054 public void log(String msg) { 055 enhanceContext.log(className, msg); 056 } 057 058 public AnnotationInfo getClassAnnotationInfo() { 059 return classAnnotationInfo; 060 } 061 062 /** 063 * Returns Transactional information from a matching interface method. 064 * <p> 065 * Returns null if no matching (transactional) interface method was found. 066 * </p> 067 * 068 * @param methodName 069 * The method name 070 * @param methodDesc 071 * The method description 072 */ 073 public AnnotationInfo getInterfaceTransactionalInfo(String methodName, String methodDesc) { 074 075 AnnotationInfo interfaceAnnotationInfo = null; 076 077 for (int i = 0; i < transactionalInterfaces.size(); i++) { 078 ClassMeta interfaceMeta = transactionalInterfaces.get(i); 079 AnnotationInfo ai = interfaceMeta.getInterfaceTransactionalInfo(methodName, methodDesc); 080 if (ai != null) { 081 if (interfaceAnnotationInfo != null) { 082 String msg = "Error in [" + className + "] searching the transactional interfaces [" 083 + transactionalInterfaces + "] found more than one match for the transactional method:" 084 + methodName + " " + methodDesc; 085 086 logger.log(Level.SEVERE, msg); 087 088 } else { 089 interfaceAnnotationInfo = ai; 090 if (isLog(2)) { 091 log("inherit transactional from interface [" + interfaceMeta + "] method[" + methodName + " " 092 + methodDesc + "]"); 093 } 094 } 095 } 096 } 097 098 return interfaceAnnotationInfo; 099 } 100 101 /** 102 * Visit the class with interfaces. 103 */ 104 @Override 105 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 106 107 className = name; 108 109 // Note: interfaces can be an empty array but not null 110 int n = 1 + interfaces.length; 111 String[] newInterfaces = new String[n]; 112 for (int i = 0; i < interfaces.length; i++) { 113 newInterfaces[i] = interfaces[i]; 114 if (newInterfaces[i].equals(EnhanceConstants.C_ENHANCEDTRANSACTIONAL)) { 115 throw new AlreadyEnhancedException(name); 116 } 117 ClassMeta interfaceMeta = enhanceContext.getInterfaceMeta(newInterfaces[i], classLoader); 118 if (interfaceMeta != null && interfaceMeta.isTransactional()) { 119 // the interface was transactional. We gather its information 120 // because our methods inherit that transactional configuration 121 transactionalInterfaces.add(interfaceMeta); 122 123 if (isLog(6)) { 124 log(" implements tranactional interface " + interfaceMeta.getDescription()); 125 } 126 } 127 } 128 129 // Add the EnhancedTransactional interface 130 newInterfaces[newInterfaces.length - 1] = EnhanceConstants.C_ENHANCEDTRANSACTIONAL; 131 132 super.visit(version, access, name, signature, superName, newInterfaces); 133 } 134 135 /** 136 * Visit class level annotations. 137 */ 138 @Override 139 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 140 141 AnnotationVisitor av = super.visitAnnotation(desc, visible); 142 143 if (desc.equals(EnhanceConstants.AVAJE_TRANSACTIONAL_ANNOTATION)) { 144 // we have class level Transactional annotation 145 // which will act as default for all methods in this class 146 classAnnotationInfo = new AnnotationInfo(null); 147 return new AnnotationInfoVisitor(null, classAnnotationInfo, av); 148 149 } else { 150 return av; 151 } 152 } 153 154 /** 155 * Visit the methods specifically looking for method level transactional 156 * annotations. 157 */ 158 @Override 159 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 160 161 MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 162 if (name.equals("<init>") || name.equals("<clinit>")) { 163 // not enhancing constructors at the moment 164 return mv; 165 } 166 return new ScopeTransAdapter(this, mv, access, name, desc); 167 } 168 169 @Override 170 public void visitEnd() { 171 if (isLog(2)) { 172 log("methods:" + transactionalMethods); 173 } 174 super.visitEnd(); 175 } 176 177 void transactionalMethod(TransactionalMethodKey methodKey) { 178 179 transactionalMethods.add(methodKey.getMethodName()); 180 if (isLog(3)) { 181 log("method - " + methodKey); 182 } 183 } 184 185 /** 186 * Create and return the TransactionalMethodKey. 187 * 188 * Takes into account the profiling mode (as per manifest) and explicit profileId. 189 */ 190 public TransactionalMethodKey createMethodKey(String methodName, String methodDesc, int profId) { 191 return enhanceContext.createMethodKey(className, methodName, methodDesc, profId); 192 } 193}