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