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.common.EnhanceConstants.TRANSACTIONAL_ANNOTATION;
009
010/**
011 * ClassAdapter used to detect if this class needs enhancement for entity or
012 * transactional support.
013 */
014public class DetectEnhancement extends ClassVisitor {
015
016  private final ClassLoader classLoader;
017
018  private final EnhanceContext enhanceContext;
019
020  private final DetectTransactionalMethod detectTransactionalMethod = new DetectTransactionalMethod();
021
022  private String className;
023
024  private boolean entity;
025
026  private boolean enhancedEntity;
027
028  private boolean transactional;
029
030  private boolean enhancedTransactional;
031
032  public DetectEnhancement(ClassLoader classLoader, EnhanceContext context) {
033    super(Opcodes.ASM7);
034    this.classLoader = classLoader;
035    this.enhanceContext = context;
036  }
037
038  private boolean isLog(int level) {
039    return enhanceContext.isLog(level);
040  }
041
042  private void log(String msg) {
043    enhanceContext.log(className, msg);
044  }
045
046  public void log(int level, String msg) {
047    if (isLog(level)) {
048      log(msg);
049    }
050  }
051
052  public boolean isEnhancedEntity() {
053    return enhancedEntity;
054  }
055
056  public boolean isEnhancedTransactional() {
057    return enhancedTransactional;
058  }
059
060  /**
061  * Return true if this is an entity bean or embeddable bean.
062  */
063  public boolean isEntity() {
064    return entity;
065  }
066
067  /**
068  * Return true if ANY method has the transactional annotation.
069  */
070  public boolean isTransactional() {
071    return transactional;
072  }
073
074  /**
075  * Visit the class with interfaces.
076  */
077  @Override
078  public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
079
080    if ((access & Opcodes.ACC_INTERFACE) != 0) {
081      throw new NoEnhancementRequiredException("Interface type");
082    }
083
084    this.className = name;
085    for (String anInterface : interfaces) {
086      if (anInterface.equals(EnhanceConstants.C_ENTITYBEAN)) {
087        enhancedEntity = true;
088        entity = true;
089
090      } else if (anInterface.equals(EnhanceConstants.C_ENHANCEDTRANSACTIONAL)) {
091        enhancedTransactional = true;
092
093      } else {
094        ClassMeta interfaceMeta = enhanceContext.getInterfaceMeta(anInterface, 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(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(TRANSACTIONAL_ANNOTATION)) {
157        transactional = true;
158      }
159      return null;
160    }
161
162  }
163}