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