All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebean.enhance.common.EnhanceContext Maven / Gradle / Ivy

package io.ebean.enhance.common;

import io.ebean.enhance.Transformer;
import io.ebean.enhance.asm.MethodVisitor;
import io.ebean.enhance.entity.MessageOutput;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static io.ebean.enhance.asm.Opcodes.INVOKEINTERFACE;
import static io.ebean.enhance.asm.Opcodes.INVOKEVIRTUAL;
import static io.ebean.enhance.common.EnhanceConstants.C_INTERCEPT_I;
import static io.ebean.enhance.common.EnhanceConstants.C_INTERCEPT_RW;

/**
 * Used to hold metadata, arguments and log levels for the enhancement.
 */
public final class EnhanceContext {

  private static final Logger logger = Logger.getLogger(EnhanceContext.class.getName());

  public enum ProfileLineNumberMode {
    /**
     * Line numbering on profile location when method + queryBean type is not unique
     */
    AUTO,
    /**
     * No line numbering on profile locations
     */
    NONE,
    /**
     * Line numbering on all profile locations
     */
    ALL
  }

  private final AgentManifest manifest;
  private final IgnoreClassHelper ignoreClassHelper;
  private final Map agentArgsMap;
  private final ClassMetaReader reader;
  private final ClassBytesReader classBytesReader;
  private MessageOutput logout;
  private int logLevel;
  private final HashMap map = new HashMap<>();
  private final FilterEntityTransactional filterEntityTransactional;
  private final FilterQueryBean filterQueryBean;
  private final PackageFilter packageFilter;
  private boolean throwOnError;
  private final boolean enableProfileLocation;
  private final boolean enableEntityFieldAccess;
  private final ProfileLineNumberMode profileLineNumberMode;
  private final int accPublic;
  private final int accProtected;
  private final int accPrivate;
  private final int enhancementVersion;
  private SummaryInfo summaryInfo;

  public EnhanceContext(ClassBytesReader classBytesReader, String agentArgs, AgentManifest manifest) {
    this(classBytesReader, agentArgs, manifest, new ClassMetaCache());
  }

  /**
   * Construct a context for enhancement.
   */
  public EnhanceContext(ClassBytesReader classBytesReader, String agentArgs, AgentManifest manifest, ClassMetaCache metaCache) {
    this.manifest = manifest;
    this.enableProfileLocation = manifest.isEnableProfileLocation();
    this.enableEntityFieldAccess = manifest.isEnableEntityFieldAccess();
    this.profileLineNumberMode = manifest.profileLineMode();
    this.accPublic = manifest.accPublic();
    this.accProtected = manifest.accProtected();
    this.accPrivate = manifest.accPrivate();
    this.agentArgsMap = ArgParser.parse(agentArgs);
    this.enhancementVersion = versionOf(manifest);
    this.filterEntityTransactional = new FilterEntityTransactional(manifest);
    this.filterQueryBean = new FilterQueryBean(manifest);
    this.ignoreClassHelper = new IgnoreClassHelper();
    this.logout = new SysoutMessageOutput(System.out);
    this.classBytesReader = classBytesReader;
    this.reader = new ClassMetaReader(this, metaCache);
    this.packageFilter = initPackageFilter(agentArgsMap.get("packages"));

    if (manifest.debugLevel() > -1) {
      logLevel = manifest.debugLevel();
    }
    String debugValue = agentArgsMap.get("debug");
    if (debugValue != null) {
      try {
        logLevel = Integer.parseInt(debugValue);
      } catch (NumberFormatException e) {
        logger.log(Level.WARNING, "Agent debug argument [" + debugValue + "] is not an int?");
      }
    }
    if (logLevel > 0 || propertyBoolean("printversion", false)) {
      System.out.println("ebean-agent version:" + Transformer.getVersion() + " enhancement:" + enhancementVersion + " resources:" + manifest.loadedResources());
    }
  }

  private PackageFilter initPackageFilter(String packages) {
    return packages == null ? null : new PackageFilter(packages);
  }

  private int versionOf(AgentManifest manifest) {
    String ver = agentArgsMap.get("version");
    if (ver != null) {
      return Integer.parseInt(ver);
    }
    return manifest.enhancementVersion();
  }

  public void withClassLoader(ClassLoader loader) {
    if (manifest.readManifest(loader)) {
      if (logLevel > 1) {
        log(null, "loaded entity packages: " + manifest.entityPackages());
      }
    }
  }

  public void setLogLevel(int logLevel) {
    this.logLevel = logLevel;
  }

  @Deprecated
  public String getPackagesSummary() {
    return packagesSummary();
  }

  /**
   * Return the summary of the packages controlling enhancement.
   */
  public String packagesSummary() {
    return "packages entity:" + entityPackages()
      + "  transactional:" + transactionalPackages()
      + "  querybean:" + querybeanPackages()
      + "  profileLocation:" + enableProfileLocation
      + "  version:" + enhancementVersion;
  }

  public Set entityPackages() {
    return manifest.entityPackages();
  }

  public Set transactionalPackages() {
    return manifest.transactionalPackages();
  }

  public Set querybeanPackages() {
    return manifest.querybeanPackages();
  }

  public byte[] classBytes(String className, ClassLoader classLoader) {
    return classBytesReader.getClassBytes(className, classLoader);
  }

  public boolean isEntityBean(String owner) {
    return manifest.isDetectEntityBean(owner);
  }

  /**
   * Return true if the owner class is a type query bean.
   * 

* If true typically means the caller needs to change GETFIELD calls to instead invoke the generated * 'property access' methods. */ public boolean isQueryBean(String owner, ClassLoader classLoader) { if (manifest.isDetectQueryBean(owner)) { try { final ClassMeta classMeta = reader.get(true, owner, classLoader); if (classMeta == null) { // For Gradle Kotlin KAPT the generate query bean bytecode // isn't available to the classLoader. Just returning true. return true; } return classMeta.isQueryBean(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } return false; } /** * Return a value from the entity arguments using its key. */ private String property(String key) { return agentArgsMap.get(key.toLowerCase()); } private boolean propertyBoolean(String key, boolean defaultValue) { String s = property(key); if (s == null) { return defaultValue; } else { return s.trim().equalsIgnoreCase("true"); } } public boolean isEnableEntityFieldAccess() { return enableEntityFieldAccess; } /** * Return true if profile location enhancement is on. */ public boolean isEnableProfileLocation() { return enableProfileLocation; } /** * Return true if this class should be scanned for transactional enhancement. */ public boolean detectEntityTransactionalEnhancement(String className) { return filterEntityTransactional.detectEnhancement(className); } /** * Return true if this class should be scanned for query bean enhancement. */ public boolean detectQueryBeanEnhancement(String className) { return filterQueryBean.detectEnhancement(className); } /** * Return true if this class should be ignored. That is JDK classes and * known libraries JDBC drivers etc can be skipped. */ public boolean isIgnoreClass(String className) { if (packageFilter != null && packageFilter.ignore(className)) { return true; } return ignoreClassHelper.isIgnoreClass(className); } /** * Change the logout to something other than system out. */ public void setLogout(MessageOutput logout) { this.logout = logout; } /** * Create a new meta object for enhancing a class. */ public ClassMeta createClassMeta() { return new ClassMeta(this, logLevel, logout); } /** * Read the class metadata for a super class. *

* Typically used to read meta data for inheritance hierarchy. */ public ClassMeta superMeta(String superClassName, ClassLoader classLoader) { try { if (isIgnoreClass(superClassName)) { return null; } return reader.get(false, superClassName, classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * Read the class metadata for an interface. *

* Typically used to check the interface to see if it is transactional. */ public ClassMeta interfaceMeta(String interfaceClassName, ClassLoader classLoader) { try { if (isIgnoreClass(interfaceClassName)) { return null; } return reader.get(true, interfaceClassName, classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public void addClassMeta(ClassMeta meta) { map.put(meta.className(), meta); } public ClassMeta get(String className) { return map.get(className); } /** * Log some debug output. */ public void log(int level, String className, String msg) { if (logLevel >= level) { log(className, msg); } } public void log(String className, String msg) { if (className != null) { msg = "cls: " + className + " msg: " + msg; } logout.println("ebean-enhance> " + msg); } public boolean isLog(int level) { return logLevel >= level; } /** * Log an error. */ public void log(Throwable e) { e.printStackTrace( new PrintStream(new ByteArrayOutputStream()) { @Override public void print(String message) { logout.println(message); } @Override public void println(String message) { logout.println(message); } }); } /** * Return the log level. */ public int logLevel() { return logLevel; } public boolean isTransientInit() { return manifest.isTransientInit(); } public boolean isTransientInitThrowError() { return manifest.isTransientInitThrowError(); } /** * Return true if internal ebean fields in entity classes should be transient. */ public boolean isTransientInternalFields() { return manifest.isTransientInternalFields(); } /** * Return true if we should add null checking on *ToMany fields. *

* On getting a many that is null Ebean will create an empty List, Set or Map. If it is a * ManyToMany it will turn on Modify listening. */ public boolean isCheckNullManyFields() { return manifest.isCheckNullManyFields(); } public boolean isAllowNullableDbArray() { return manifest.isAllowNullableDbArray(); } /** * Return true if transform should throw exception rather than log and return null. */ public boolean isThrowOnError() { return throwOnError; } /** * Set to true if you want transform to throw exceptions rather than return null. */ public void setThrowOnError(boolean throwOnError) { this.throwOnError = throwOnError; } /** * Turn on the summary collection of the enhancement. */ public void collectSummary() { this.summaryInfo = new SummaryInfo(manifest.loadedResources()); } /** * Add the transactional enhanced class to summary information. */ public void summaryTransactional(String className) { if (summaryInfo != null) { summaryInfo.addTransactional(className); } } /** * Add the entity enhanced class to summary information. */ public void summaryEntity(String className) { if (summaryInfo != null) { summaryInfo.addEntity(className); } } /** * Add the query bean enhanced class to summary information. */ public void summaryQueryBean(String className) { if (summaryInfo != null) { summaryInfo.addQueryBean(className); } } /** * Add the query bean caller enhanced class to summary information. */ public void summaryQueryBeanCaller(String className) { if (summaryInfo != null) { summaryInfo.addQueryBeanCaller(className); } } /** * Add the enhanced class with field access replacement to summary information. */ public void summaryFieldAccessUser(String className) { if (summaryInfo != null) { summaryInfo.addFieldAccessUser(className); } } @Deprecated public SummaryInfo getSummaryInfo() { return summaryInfo(); } /** * Return the summary of the enhancement. *

* Note that collectSummary() must be called in order for summary * information to be collected and returned here. */ public SummaryInfo summaryInfo() { return summaryInfo.prepare(); } public int accPublic() { return accPublic; } public int accProtected() { return accProtected; } public int accPrivate() { return accPrivate; } public boolean isToManyGetField() { return enhancementVersion > 128; } public boolean isEnhancedToString() { return enhancementVersion > 132; } public String interceptNew() { return enhancementVersion >= 140 ? C_INTERCEPT_RW : C_INTERCEPT_I; } public void visitMethodInsnIntercept(MethodVisitor mv, String name, String desc) { mv.visitMethodInsn(interceptInvoke(), C_INTERCEPT_I, name, desc, interceptIface()); } private int interceptInvoke() { return enhancementVersion >= 140 ? INVOKEINTERFACE : INVOKEVIRTUAL; } private boolean interceptIface() { return enhancementVersion >= 140; } public boolean interceptAddReadOnly() { return enhancementVersion >= 141; } public boolean supportsProfileWithLine() { return enhancementVersion >= 143; // Ebean 13.13.1 onwards } public boolean improvedQueryBeans() { return enhancementVersion >= 145; } public boolean fluidQueryBuilders() { return enhancementVersion >= 148; } public ProfileLineNumberMode profileLineMode() { return profileLineNumberMode; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy