001package io.ebean.enhance.common;
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;
007
008/**
009 * ClassAdapter used to detect if this class needs enhancement for entity or
010 * transactional support.
011 */
012public class DetectEnhancement extends ClassVisitor {
013
014  private final ClassLoader classLoader;
015
016  private final EnhanceContext enhanceContext;
017
018  private final DetectTransactionalMethod detectTransactionalMethod = new DetectTransactionalMethod();
019
020  private String className;
021
022  private boolean entity;
023
024  private boolean enhancedEntity;
025
026  private boolean transactional;
027
028  private boolean enhancedTransactional;
029
030  public DetectEnhancement(ClassLoader classLoader, EnhanceContext context) {
031    super(Opcodes.ASM7);
032    this.classLoader = classLoader;
033    this.enhanceContext = context;
034  }
035
036  public boolean isLog(int level) {
037    return enhanceContext.isLog(level);
038  }
039
040  public void log(String msg) {
041    enhanceContext.log(className, msg);
042  }
043
044  public void log(int level, String msg) {
045    if (isLog(level)) {
046      log(msg);
047    }
048  }
049
050  public boolean isEnhancedEntity() {
051    return enhancedEntity;
052  }
053
054  public boolean isEnhancedTransactional() {
055    return enhancedTransactional;
056  }
057
058  /**
059  * Return true if this is an entity bean or embeddable bean.
060  */
061  public boolean isEntity() {
062    return entity;
063  }
064
065  /**
066  * Return true if ANY method has the transactional annotation.
067  */
068  public boolean isTransactional() {
069    return transactional;
070  }
071
072  /**
073  * Visit the class with interfaces.
074  */
075  @Override
076  public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
077
078    if ((access & Opcodes.ACC_INTERFACE) != 0) {
079      throw new NoEnhancementRequiredException("Interface type");
080    }
081
082    this.className = name;
083
084    for (int i = 0; i < interfaces.length; i++) {
085
086      if (interfaces[i].equals(EnhanceConstants.C_ENTITYBEAN)) {
087        enhancedEntity = true;
088        entity = true;
089
090      } else if (interfaces[i].equals(EnhanceConstants.C_ENHANCEDTRANSACTIONAL)) {
091        enhancedTransactional = true;
092
093      } else {
094        ClassMeta interfaceMeta = enhanceContext.getInterfaceMeta(interfaces[i], classLoader);
095        if (interfaceMeta != null && interfaceMeta.isTransactional()) {
096          transactional = true;
097          if (isLog(9)) {
098            log("detected implements transactional interface " + interfaceMeta);
099          }
100        }
101      }
102    }
103
104    if (isLog(4)) {
105      log("interfaces:  enhancedEntity[" + enhancedEntity + "] transactional[" + enhancedTransactional + "]");
106    }
107  }
108
109  /**
110  * Visit class level annotations.
111  */
112  @Override
113  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
114    if (isEntityAnnotation(desc)) {
115      if (isLog(5)) {
116        log("found entity annotation " + desc);
117      }
118      entity = true;
119
120    } else if (desc.equals(EnhanceConstants.AVAJE_TRANSACTIONAL_ANNOTATION)) {
121      if (isLog(5)) {
122        log("found transactional annotation " + desc);
123      }
124      transactional = true;
125    }
126    return null;
127  }
128
129  /**
130  * Return true if the annotation is for an Entity, Embeddable or MappedSuperclass.
131  */
132  private boolean isEntityAnnotation(String desc) {
133    return EntityCheck.isEntityAnnotation(desc);
134  }
135
136  /**
137  * Visit the methods specifically looking for method level transactional
138  * annotations.
139  */
140  @Override
141  public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
142    return detectTransactionalMethod;
143  }
144
145  /**
146  * Check methods for Transactional annotation.
147  */
148  private class DetectTransactionalMethod extends MethodVisitor {
149
150    DetectTransactionalMethod() {
151      super(Opcodes.ASM7);
152    }
153
154    @Override
155    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
156      if (desc.equals(EnhanceConstants.AVAJE_TRANSACTIONAL_ANNOTATION)) {
157        transactional = true;
158      }
159      return null;
160    }
161
162  }
163}